Eevee: Shadows: Add Contact Shadows
[blender.git] / source / blender / draw / engines / eevee / eevee_lights.c
index 2bd33f0a75527223790eddfcff4042341d32a55e..8138b9a0ffd8b0baf610ab05be620d6a81a52542 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "DRW_render.h"
 
+#include "BLI_dynstr.h"
+
 #include "BKE_object.h"
 
 #include "eevee_engine.h"
@@ -57,12 +59,16 @@ static struct {
        struct GPUShader *shadow_sh;
        struct GPUShader *shadow_store_cube_sh[SHADOW_METHOD_MAX];
        struct GPUShader *shadow_store_cascade_sh[SHADOW_METHOD_MAX];
+       struct GPUShader *shadow_copy_cube_sh[SHADOW_METHOD_MAX];
+       struct GPUShader *shadow_copy_cascade_sh[SHADOW_METHOD_MAX];
 } e_data = {NULL}; /* Engine data */
 
 extern char datatoc_shadow_vert_glsl[];
 extern char datatoc_shadow_geom_glsl[];
 extern char datatoc_shadow_frag_glsl[];
 extern char datatoc_shadow_store_frag_glsl[];
+extern char datatoc_shadow_copy_frag_glsl[];
+extern char datatoc_concentric_samples_lib_glsl[];
 
 /* *********** FUNCTIONS *********** */
 
@@ -80,13 +86,33 @@ void EEVEE_lights_init(EEVEE_SceneLayerData *sldata)
                e_data.shadow_sh = DRW_shader_create(
                        datatoc_shadow_vert_glsl, datatoc_shadow_geom_glsl, datatoc_shadow_frag_glsl, NULL);
 
-               e_data.shadow_store_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define ESM\n");
-               e_data.shadow_store_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define ESM\n"
-                                                                                                                         "#define CSM\n");
-
-               e_data.shadow_store_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define VSM\n");
-               e_data.shadow_store_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define VSM\n"
-                                                                                                                         "#define CSM\n");
+               DynStr *ds_frag = BLI_dynstr_new();
+               BLI_dynstr_append(ds_frag, datatoc_concentric_samples_lib_glsl);
+               BLI_dynstr_append(ds_frag, datatoc_shadow_store_frag_glsl);
+               char *store_shadow_shader_str = BLI_dynstr_get_cstring(ds_frag);
+               BLI_dynstr_free(ds_frag);
+
+               e_data.shadow_store_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(store_shadow_shader_str, "#define ESM\n");
+               e_data.shadow_store_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(store_shadow_shader_str, "#define ESM\n"
+                                                                                                                  "#define CSM\n");
+
+               e_data.shadow_store_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(store_shadow_shader_str, "#define VSM\n");
+               e_data.shadow_store_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(store_shadow_shader_str, "#define VSM\n"
+                                                                                                                  "#define CSM\n");
+
+               MEM_freeN(store_shadow_shader_str);
+
+               e_data.shadow_copy_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(datatoc_shadow_copy_frag_glsl, "#define ESM\n"
+                                                                                                                    "#define COPY\n");
+               e_data.shadow_copy_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(datatoc_shadow_copy_frag_glsl, "#define ESM\n"
+                                                                                                                       "#define COPY\n"
+                                                                                                                       "#define CSM\n");
+
+               e_data.shadow_copy_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(datatoc_shadow_copy_frag_glsl, "#define VSM\n"
+                                                                                                                    "#define COPY\n");
+               e_data.shadow_copy_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(datatoc_shadow_copy_frag_glsl, "#define VSM\n"
+                                                                                                                       "#define COPY\n"
+                                                                                                                       "#define CSM\n");
        }
 
        if (!sldata->lamps) {
@@ -109,6 +135,8 @@ void EEVEE_lights_init(EEVEE_SceneLayerData *sldata)
                DRW_TEXTURE_FREE_SAFE(sldata->shadow_pool);
                DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_target);
                DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_target);
+               DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_blur);
+               DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_blur);
 
                linfo->shadow_high_bitdepth = sh_high_bitdepth;
                linfo->shadow_method = sh_method;
@@ -143,7 +171,7 @@ void EEVEE_lights_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                psl->shadow_cube_store_pass = DRW_pass_create("Shadow Storage Pass", DRW_STATE_WRITE_COLOR);
 
                DRWShadingGroup *grp = DRW_shgroup_create(e_data.shadow_store_cube_sh[linfo->shadow_method], psl->shadow_cube_store_pass);
-               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cube_target);
+               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cube_blur);
                DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
                DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
                DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
@@ -153,13 +181,35 @@ void EEVEE_lights_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                psl->shadow_cascade_store_pass = DRW_pass_create("Shadow Cascade Storage Pass", DRW_STATE_WRITE_COLOR);
 
                DRWShadingGroup *grp = DRW_shgroup_create(e_data.shadow_store_cascade_sh[linfo->shadow_method], psl->shadow_cascade_store_pass);
-               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cascade_target);
+               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cascade_blur);
                DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
                DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
                DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
                DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
        }
 
+       {
+               psl->shadow_cube_copy_pass = DRW_pass_create("Shadow Copy Pass", DRW_STATE_WRITE_COLOR);
+
+               DRWShadingGroup *grp = DRW_shgroup_create(e_data.shadow_copy_cube_sh[linfo->shadow_method], psl->shadow_cube_copy_pass);
+               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cube_target);
+               DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
+               DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
+               DRW_shgroup_uniform_int(grp, "faceId", &linfo->current_shadow_face, 1);
+               DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
+       }
+
+       {
+               psl->shadow_cascade_copy_pass = DRW_pass_create("Shadow Cascade Copy Pass", DRW_STATE_WRITE_COLOR);
+
+               DRWShadingGroup *grp = DRW_shgroup_create(e_data.shadow_copy_cascade_sh[linfo->shadow_method], psl->shadow_cascade_copy_pass);
+               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cascade_target);
+               DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
+               DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
+               DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
+               DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
+       }
+
        {
                psl->shadow_cube_pass = DRW_pass_create("Shadow Cube Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
        }
@@ -177,9 +227,9 @@ void EEVEE_lights_cache_add(EEVEE_SceneLayerData *sldata, Object *ob)
        EEVEE_LampsInfo *linfo = sldata->lamps;
 
        /* Step 1 find all lamps in the scene and setup them */
-       if (linfo->num_light > MAX_LIGHT) {
+       if (linfo->num_light >= MAX_LIGHT) {
                printf("Too much lamps in the scene !!!\n");
-               linfo->num_light = MAX_LIGHT;
+               linfo->num_light = MAX_LIGHT - 1;
        }
        else {
                Lamp *la = (Lamp *)ob->data;
@@ -270,9 +320,10 @@ void EEVEE_lights_cache_shcaster_add(EEVEE_SceneLayerData *sldata, EEVEE_PassLis
 }
 
 void EEVEE_lights_cache_shcaster_material_add(
-       EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, struct GPUMaterial *gpumat, struct Gwn_Batch *geom, float (*obmat)[4], float *alpha_threshold)
+       EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, struct GPUMaterial *gpumat,
+       struct Gwn_Batch *geom, struct Object *ob, float (*obmat)[4], float *alpha_threshold)
 {
-       DRWShadingGroup *grp = DRW_shgroup_material_instance_create(gpumat, psl->shadow_cube_pass, geom);
+       DRWShadingGroup *grp = DRW_shgroup_material_instance_create(gpumat, psl->shadow_cube_pass, geom, ob);
 
        if (grp == NULL) return;
 
@@ -285,7 +336,7 @@ void EEVEE_lights_cache_shcaster_material_add(
        for (int i = 0; i < 6; ++i)
                DRW_shgroup_call_dynamic_add_empty(grp);
 
-       grp = DRW_shgroup_material_instance_create(gpumat, psl->shadow_cascade_pass, geom);
+       grp = DRW_shgroup_material_instance_create(gpumat, psl->shadow_cascade_pass, geom, ob);
        DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
        DRW_shgroup_uniform_mat4(grp, "ShadowModelMatrix", (float *)obmat);
 
@@ -321,12 +372,15 @@ void EEVEE_lights_cache_finish(EEVEE_SceneLayerData *sldata)
                /* TODO render everything on the same 2d render target using clip planes and no Geom Shader. */
                /* Cubemaps */
                sldata->shadow_cube_target = DRW_texture_create_cube(linfo->shadow_cube_target_size, DRW_TEX_DEPTH_24, 0, NULL);
+               sldata->shadow_cube_blur = DRW_texture_create_cube(linfo->shadow_cube_target_size, shadow_pool_format, DRW_TEX_FILTER, NULL);
        }
 
        if (!sldata->shadow_cascade_target) {
                /* CSM */
                sldata->shadow_cascade_target = DRW_texture_create_2D_array(
                        linfo->shadow_size, linfo->shadow_size, MAX_CASCADE_NUM, DRW_TEX_DEPTH_24, 0, NULL);
+               sldata->shadow_cascade_blur = DRW_texture_create_2D_array(
+                       linfo->shadow_size, linfo->shadow_size, MAX_CASCADE_NUM, shadow_pool_format, DRW_TEX_FILTER, NULL);
        }
 
        /* Initialize Textures Array first so DRW_framebuffer_init just bind them. */
@@ -455,6 +509,11 @@ static void eevee_shadow_cube_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_La
        ubo_data->shadow_start = (float)(sh_data->layer_id);
        ubo_data->data_start = (float)(sh_data->cube_id);
        ubo_data->multi_shadow_count = (float)(sh_nbr);
+
+       ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
+       ubo_data->contact_bias = 0.05f * la->contact_bias;
+       ubo_data->contact_spread = la->contact_spread;
+       ubo_data->contact_thickness = la->contact_thickness;
 }
 
 #define LERP(t, a, b) ((a) + (t) * ((b) - (a)))
@@ -493,10 +552,12 @@ static void frustum_min_bounding_sphere(const float corners[8][4], float r_cente
 
 static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_LampEngineData *led)
 {
+       Lamp *la = (Lamp *)ob->data;
+
        /* Camera Matrices */
        float persmat[4][4], persinv[4][4];
        float viewprojmat[4][4], projinv[4][4];
-       float near, far;
+       float view_near, view_far;
        float near_v[4] = {0.0f, 0.0f, -1.0f, 1.0f};
        float far_v[4] = {0.0f, 0.0f,  1.0f, 1.0f};
        bool is_persp = DRW_viewport_is_persp_get();
@@ -507,75 +568,134 @@ static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE
        invert_m4_m4(projinv, viewprojmat);
        mul_m4_v4(projinv, near_v);
        mul_m4_v4(projinv, far_v);
-       near = near_v[2];
-       far = far_v[2]; /* TODO: Should be a shadow parameter */
+       view_near = near_v[2];
+       view_far = far_v[2]; /* TODO: Should be a shadow parameter */
        if (is_persp) {
-               near /= near_v[3];
-               far /= far_v[3];
+               view_near /= near_v[3];
+               view_far /= far_v[3];
        }
 
        /* Lamps Matrices */
        float viewmat[4][4], projmat[4][4];
        int sh_nbr = 1; /* TODO : MSM */
-       int cascade_nbr = MAX_CASCADE_NUM; /* TODO : Custom cascade number */
+       int cascade_nbr = la->cascade_count;
 
        EEVEE_ShadowCascadeData *sh_data = (EEVEE_ShadowCascadeData *)led->storage;
        EEVEE_Light *evli = linfo->light_data + sh_data->light_id;
        EEVEE_Shadow *ubo_data = linfo->shadow_data + sh_data->shadow_id;
        EEVEE_ShadowCascade *cascade_data = linfo->shadow_cascade_data + sh_data->cascade_id;
-       Lamp *la = (Lamp *)ob->data;
 
        /* The technique consists into splitting
         * the view frustum into several sub-frustum
         * that are individually receiving one shadow map */
 
+       float csm_start, csm_end;
+
+       if (is_persp) {
+               csm_start = view_near;
+               csm_end = max_ff(view_far, -la->cascade_max_dist);
+               /* Avoid artifacts */
+               csm_end = min_ff(view_near, csm_end);
+       }
+       else {
+               csm_start = -view_far;
+               csm_end = view_far;
+       }
+
        /* init near/far */
        for (int c = 0; c < MAX_CASCADE_NUM; ++c) {
-               cascade_data->split[c] = far;
+               cascade_data->split_start[c] = csm_end;
+               cascade_data->split_end[c] = csm_end;
        }
 
        /* Compute split planes */
-       float splits_ndc[MAX_CASCADE_NUM + 1];
-       splits_ndc[0] = -1.0f;
-       splits_ndc[cascade_nbr] = 1.0f;
-       for (int c = 1; c < cascade_nbr; ++c) {
-               const float lambda = 0.8f; /* TODO : Parameter */
+       float splits_start_ndc[MAX_CASCADE_NUM];
+       float splits_end_ndc[MAX_CASCADE_NUM];
 
+       {
+               /* Nearest plane */
+               float p[4] = {1.0f, 1.0f, csm_start, 1.0f};
+               /* TODO: we don't need full m4 multiply here */
+               mul_m4_v4(viewprojmat, p);
+               splits_start_ndc[0] = p[2];
+               if (is_persp) {
+                       splits_start_ndc[0] /= p[3];
+               }
+       }
+
+       {
+               /* Farthest plane */
+               float p[4] = {1.0f, 1.0f, csm_end, 1.0f};
+               /* TODO: we don't need full m4 multiply here */
+               mul_m4_v4(viewprojmat, p);
+               splits_end_ndc[cascade_nbr - 1] = p[2];
+               if (is_persp) {
+                       splits_end_ndc[cascade_nbr - 1] /= p[3];
+               }
+       }
+
+       cascade_data->split_start[0] = csm_start;
+       cascade_data->split_end[cascade_nbr - 1] = csm_end;
+
+       for (int c = 1; c < cascade_nbr; ++c) {
                /* View Space */
-               float linear_split = LERP(((float)(c) / (float)cascade_nbr), near, far);
-               float exp_split = near * powf(far / near, (float)(c) / (float)cascade_nbr);
+               float linear_split = LERP(((float)(c) / (float)cascade_nbr), csm_start, csm_end);
+               float exp_split = csm_start * powf(csm_end / csm_start, (float)(c) / (float)cascade_nbr);
 
                if (is_persp) {
-                       cascade_data->split[c-1] = LERP(lambda, linear_split, exp_split);
+                       cascade_data->split_start[c] = LERP(la->cascade_exponent, linear_split, exp_split);
                }
                else {
-                       cascade_data->split[c-1] = linear_split;
+                       cascade_data->split_start[c] = linear_split;
                }
+               cascade_data->split_end[c-1] = cascade_data->split_start[c];
+
+               /* Add some overlap for smooth transition */
+               cascade_data->split_start[c] = LERP(la->cascade_fade, cascade_data->split_end[c-1],
+                                                   (c > 1) ? cascade_data->split_end[c-2] : cascade_data->split_start[0]);
 
                /* NDC Space */
-               float p[4] = {1.0f, 1.0f, cascade_data->split[c-1], 1.0f};
-               mul_m4_v4(viewprojmat, p);
-               splits_ndc[c] = p[2];
+               {
+                       float p[4] = {1.0f, 1.0f, cascade_data->split_start[c], 1.0f};
+                       /* TODO: we don't need full m4 multiply here */
+                       mul_m4_v4(viewprojmat, p);
+                       splits_start_ndc[c] = p[2];
 
-               if (is_persp) {
-                       splits_ndc[c] /= p[3];
+                       if (is_persp) {
+                               splits_start_ndc[c] /= p[3];
+                       }
+               }
+
+               {
+                       float p[4] = {1.0f, 1.0f, cascade_data->split_end[c-1], 1.0f};
+                       /* TODO: we don't need full m4 multiply here */
+                       mul_m4_v4(viewprojmat, p);
+                       splits_end_ndc[c-1] = p[2];
+
+                       if (is_persp) {
+                               splits_end_ndc[c-1] /= p[3];
+                       }
                }
        }
 
+       /* Set last cascade split fade distance into the first split_start. */
+       float prev_split = (cascade_nbr > 1) ? cascade_data->split_end[cascade_nbr-2] : cascade_data->split_start[0];
+       cascade_data->split_start[0] = LERP(la->cascade_fade, cascade_data->split_end[cascade_nbr-1], prev_split);
+
        /* For each cascade */
        for (int c = 0; c < cascade_nbr; ++c) {
                /* Given 8 frustum corners */
                float corners[8][4] = {
                        /* Near Cap */
-                       {-1.0f, -1.0f, splits_ndc[c], 1.0f},
-                       { 1.0f, -1.0f, splits_ndc[c], 1.0f},
-                       {-1.0f,  1.0f, splits_ndc[c], 1.0f},
-                       { 1.0f,  1.0f, splits_ndc[c], 1.0f},
+                       {-1.0f, -1.0f, splits_start_ndc[c], 1.0f},
+                       { 1.0f, -1.0f, splits_start_ndc[c], 1.0f},
+                       {-1.0f,  1.0f, splits_start_ndc[c], 1.0f},
+                       { 1.0f,  1.0f, splits_start_ndc[c], 1.0f},
                        /* Far Cap */
-                       {-1.0f, -1.0f, splits_ndc[c+1], 1.0f},
-                       { 1.0f, -1.0f, splits_ndc[c+1], 1.0f},
-                       {-1.0f,  1.0f, splits_ndc[c+1], 1.0f},
-                       { 1.0f,  1.0f, splits_ndc[c+1], 1.0f}
+                       {-1.0f, -1.0f, splits_end_ndc[c], 1.0f},
+                       { 1.0f, -1.0f, splits_end_ndc[c], 1.0f},
+                       {-1.0f,  1.0f, splits_end_ndc[c], 1.0f},
+                       { 1.0f,  1.0f, splits_end_ndc[c], 1.0f}
                };
 
                /* Transform them into world space */
@@ -585,6 +705,7 @@ static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE
                        corners[i][3] = 1.0f;
                }
 
+
                /* Project them into light space */
                invert_m4_m4(viewmat, ob->obmat);
                normalize_v3(viewmat[0]);
@@ -634,6 +755,11 @@ static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE
        ubo_data->shadow_start = (float)(sh_data->layer_id);
        ubo_data->data_start = (float)(sh_data->cascade_id);
        ubo_data->multi_shadow_count = (float)(sh_nbr);
+
+       ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
+       ubo_data->contact_bias = 0.05f * la->contact_bias;
+       ubo_data->contact_spread = la->contact_spread;
+       ubo_data->contact_thickness = la->contact_thickness;
 }
 
 /* Used for checking if object is inside the shadow volume. */
@@ -758,14 +884,6 @@ void EEVEE_lights_update(EEVEE_SceneLayerData *sldata)
                eevee_shadow_cube_setup(ob, linfo, led);
                delete_pruned_shadowcaster(led);
        }
-
-       for (i = 0; (ob = linfo->shadow_cascade_ref[i]) && (i < MAX_SHADOW_CASCADE); i++) {
-               EEVEE_LampEngineData *led = EEVEE_lamp_data_get(ob);
-               eevee_shadow_cascade_setup(ob, linfo, led);
-       }
-
-       DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data);
-       DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */
 }
 
 /* this refresh lamps shadow buffers */
@@ -777,6 +895,7 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
        float clear_col[4] = {FLT_MAX};
 
        /* Cube Shadow Maps */
+       DRW_stats_group_start("Cube Shadow Maps");
        DRW_framebuffer_texture_attach(sldata->shadow_target_fb, sldata->shadow_cube_target, 0, 0);
        /* Render each shadow to one layer of the array */
        for (i = 0; (ob = linfo->shadow_cube_ref[i]) && (i < MAX_SHADOW_CUBE); i++) {
@@ -788,12 +907,10 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
 
                if (led->need_update) {
                        EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
+                       EEVEE_ShadowCubeData *evscd = (EEVEE_ShadowCubeData *)led->storage;
 
-                       srd->shadow_samples_ct = 32.0f;
-                       srd->shadow_inv_samples_ct = 1.0f / srd->shadow_samples_ct;
                        srd->clip_near = la->clipsta;
                        srd->clip_far = la->clipend;
-                       linfo->filter_size = la->soft * 0.0005f;
                        copy_v3_v3(srd->position, ob->obmat[3]);
                        for (int j = 0; j < 6; j++) {
                                float tmp[4][4];
@@ -812,8 +929,45 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                        /* Render shadow cube */
                        DRW_draw_pass(psl->shadow_cube_pass);
 
+                       /* 0.001f is arbitrary, but it should be relatively small so that filter size is not too big. */
+                       float filter_texture_size = la->soft * 0.001f;
+                       float filter_pixel_size = ceil(filter_texture_size / linfo->shadow_render_data.cube_texel_size);
+                       linfo->filter_size = linfo->shadow_render_data.cube_texel_size * ((filter_pixel_size > 1.0f) ? 1.5f : 0.0f);
+
+                       /* TODO: OPTI: Filter all faces in one/two draw call */
+                       for (linfo->current_shadow_face = 0;
+                            linfo->current_shadow_face < 6;
+                            linfo->current_shadow_face++)
+                       {
+                               /* Copy using a small 3x3 box filter */
+                               DRW_framebuffer_cubeface_attach(sldata->shadow_store_fb, sldata->shadow_cube_blur, 0, linfo->current_shadow_face, 0);
+                               DRW_framebuffer_bind(sldata->shadow_store_fb);
+                               DRW_draw_pass(psl->shadow_cube_copy_pass);
+                               DRW_framebuffer_texture_detach(sldata->shadow_cube_blur);
+                       }
+
                        /* Push it to shadowmap array */
-                       DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, i, 0);
+
+                       /* Adjust constants if concentric samples change. */
+                       const float max_filter_size = 7.5f;
+                       const float previous_box_filter_size = 9.0f; /* Dunno why but that works. */
+                       const int max_sample = 256;
+
+                       if (filter_pixel_size > 2.0f) {
+                               linfo->filter_size = linfo->shadow_render_data.cube_texel_size * max_filter_size * previous_box_filter_size;
+                               filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
+                               /* Compute number of concentric samples. Depends directly on filter size. */
+                               float pix_size_sqr = filter_pixel_size * filter_pixel_size;
+                               srd->shadow_samples_ct = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
+                       }
+                       else {
+                               linfo->filter_size = 0.0f;
+                               srd->shadow_samples_ct = 4;
+                       }
+                       srd->shadow_inv_samples_ct = 1.0f / (float)srd->shadow_samples_ct;
+                       DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
+
+                       DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, evscd->layer_id, 0);
                        DRW_framebuffer_bind(sldata->shadow_store_fb);
                        DRW_draw_pass(psl->shadow_cube_store_pass);
 
@@ -823,8 +977,10 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
        linfo->update_flag &= ~LIGHT_UPDATE_SHADOW_CUBE;
 
        DRW_framebuffer_texture_detach(sldata->shadow_cube_target);
+       DRW_stats_group_end();
 
        /* Cascaded Shadow Maps */
+       DRW_stats_group_start("Cascaded Shadow Maps");
        DRW_framebuffer_texture_attach(sldata->shadow_target_fb, sldata->shadow_cascade_target, 0, 0);
        for (i = 0; (ob = linfo->shadow_cascade_ref[i]) && (i < MAX_SHADOW_CASCADE); i++) {
                EEVEE_LampEngineData *led = EEVEE_lamp_data_get(ob);
@@ -833,11 +989,11 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                EEVEE_ShadowCascadeData *evscd = (EEVEE_ShadowCascadeData *)led->storage;
                EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
 
-               srd->shadow_samples_ct = 32.0f;
-               srd->shadow_inv_samples_ct = 1.0f / srd->shadow_samples_ct;
+               eevee_shadow_cascade_setup(ob, linfo, led);
+
                srd->clip_near = la->clipsta;
                srd->clip_far = la->clipend;
-               for (int j = 0; j < MAX_CASCADE_NUM; ++j) {
+               for (int j = 0; j < la->cascade_count; ++j) {
                        copy_m4_m4(srd->shadowmat[j], evscd->viewprojmat[j]);
                }
                DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
@@ -848,13 +1004,43 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                /* Render shadow cascades */
                DRW_draw_pass(psl->shadow_cascade_pass);
 
+               /* TODO: OPTI: Filter all cascade in one/two draw call */
                for (linfo->current_shadow_cascade = 0;
-                    linfo->current_shadow_cascade < MAX_CASCADE_NUM;
+                    linfo->current_shadow_cascade < la->cascade_count;
                     ++linfo->current_shadow_cascade)
                {
-                       linfo->filter_size = la->soft * 0.0005f / (evscd->radius[linfo->current_shadow_cascade] * 0.05f);
+                       /* 0.01f factor to convert to percentage */
+                       float filter_texture_size = la->soft * 0.01f / evscd->radius[linfo->current_shadow_cascade];
+                       float filter_pixel_size = ceil(linfo->shadow_size * filter_texture_size);
+
+                       /* Copy using a small 3x3 box filter */
+                       linfo->filter_size = linfo->shadow_render_data.stored_texel_size * ((filter_pixel_size > 1.0f) ? 1.0f : 0.0f);
+                       DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_cascade_blur, 0, linfo->current_shadow_cascade, 0);
+                       DRW_framebuffer_bind(sldata->shadow_store_fb);
+                       DRW_draw_pass(psl->shadow_cascade_copy_pass);
+                       DRW_framebuffer_texture_detach(sldata->shadow_cascade_blur);
+
+                       /* Push it to shadowmap array and blur more */
+
+                       /* Adjust constants if concentric samples change. */
+                       const float max_filter_size = 7.5f;
+                       const float previous_box_filter_size = 3.2f; /* Arbitrary: less banding */
+                       const int max_sample = 256;
+
+                       if (filter_pixel_size > 2.0f) {
+                               linfo->filter_size = linfo->shadow_render_data.stored_texel_size * max_filter_size * previous_box_filter_size;
+                               filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
+                               /* Compute number of concentric samples. Depends directly on filter size. */
+                               float pix_size_sqr = filter_pixel_size * filter_pixel_size;
+                               srd->shadow_samples_ct = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
+                       }
+                       else {
+                               linfo->filter_size = 0.0f;
+                               srd->shadow_samples_ct = 4;
+                       }
+                       srd->shadow_inv_samples_ct = 1.0f / (float)srd->shadow_samples_ct;
+                       DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
 
-                       /* Push it to shadowmap array */
                        int layer = evscd->layer_id + linfo->current_shadow_cascade;
                        DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, layer, 0);
                        DRW_framebuffer_bind(sldata->shadow_store_fb);
@@ -862,7 +1048,11 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                }
        }
 
-       DRW_framebuffer_texture_detach(sldata->shadow_cube_target);
+       DRW_framebuffer_texture_detach(sldata->shadow_cascade_target);
+       DRW_stats_group_end();
+
+       DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data);
+       DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */
 }
 
 void EEVEE_lights_free(void)
@@ -871,5 +1061,7 @@ void EEVEE_lights_free(void)
        for (int i = 0; i < SHADOW_METHOD_MAX; ++i) {
                DRW_SHADER_FREE_SAFE(e_data.shadow_store_cube_sh[i]);
                DRW_SHADER_FREE_SAFE(e_data.shadow_store_cascade_sh[i]);
+               DRW_SHADER_FREE_SAFE(e_data.shadow_copy_cube_sh[i]);
+               DRW_SHADER_FREE_SAFE(e_data.shadow_copy_cascade_sh[i]);
        }
 }
\ No newline at end of file