diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-04-21 17:43:14 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-04-21 17:43:35 +0300 |
commit | 8ac1f03f411c98efe4ca4d568a2b231ff59b2505 (patch) | |
tree | c8d04a38b5dd83a90100e8819db90186c84830f5 /source/blender/draw/engines/eevee/eevee_lights.c | |
parent | cc2d501642555616f3e8808bdbaa72f9c752e1a1 (diff) |
Eevee: Cascaded Shadow Maps, follow up.
- Compute coarse bounding box of split frustum. Can be improved
- Make use of 4 cascade.
- View dependant glitches are fixed.
- Optimized shader code.
Diffstat (limited to 'source/blender/draw/engines/eevee/eevee_lights.c')
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_lights.c | 146 |
1 files changed, 121 insertions, 25 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index 7c4a5523ecd..cb4032f198c 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -295,14 +295,65 @@ static void eevee_shadow_map_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_Lam #define LERP(t, a, b) ((a) + (t) * ((b) - (a))) +static void frustum_min_bounding_sphere(const float corners[8][4], float r_center[3], float *r_radius) +{ +#if 0 /* Simple solution but waist too much space. */ + float minvec[3], maxvec[3]; + + /* compute the bounding box */ + INIT_MINMAX(minvec, maxvec); + for (int i = 0; i < 8; ++i) { + minmax_v3v3_v3(minvec, maxvec, corners[i]); + } + + /* compute the bounding sphere of this box */ + r_radius = len_v3v3(minvec, maxvec) * 0.5f; + add_v3_v3v3(r_center, minvec, maxvec); + mul_v3_fl(r_center, 0.5f); +#else + /* Make the bouding sphere always centered on the front diagonal */ + add_v3_v3v3(r_center, corners[4], corners[7]); + mul_v3_fl(r_center, 0.5f); + *r_radius = len_v3v3(corners[0], r_center); + + /* Search the largest distance between the sphere center + * and the front plane corners. */ + for (int i = 0; i < 4; ++i) { + float rad = len_v3v3(corners[4+i], r_center); + if (rad > *r_radius) { + *r_radius = rad; + } + } +#endif +} + static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_LampEngineData *led) { /* Camera Matrices */ float persmat[4][4], persinv[4][4]; + float viewprojmat[4][4], projinv[4][4]; + float near, 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(); + DRW_viewport_matrix_get(persmat, DRW_MAT_PERS); + invert_m4_m4(persinv, persmat); + /* FIXME : Get near / far from Draw manager? */ + DRW_viewport_matrix_get(viewprojmat, DRW_MAT_WIN); + 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 */ + if (is_persp) { + near /= near_v[3]; + far /= far_v[3]; + } + /* Lamps Matrices */ float viewmat[4][4], projmat[4][4]; - float minvec[3], maxvec[3]; int cascade_ct = MAX_CASCADE_NUM; + float shadow_res = 512.0f; /* TODO parameter */ EEVEE_ShadowCascadeData *evscp = (EEVEE_ShadowCascadeData *)led->sto; EEVEE_Light *evli = linfo->light_data + evscp->light_id; @@ -313,28 +364,56 @@ static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE * the view frustum into several sub-frustum * that are individually receiving one shadow map */ + /* init near/far */ + for (int c = 0; c < MAX_CASCADE_NUM; ++c) { + evsh->split[c] = far; + } + + /* Compute split planes */ + float splits_ndc[MAX_CASCADE_NUM + 1]; + splits_ndc[0] = -1.0f; + splits_ndc[cascade_ct] = 1.0f; + for (int c = 1; c < cascade_ct; ++c) { + const float lambda = 0.8f; /* TODO : Parameter */ + + /* View Space */ + float linear_split = LERP(((float)(c) / (float)cascade_ct), near, far); + float exp_split = near * powf(far / near, (float)(c) / (float)cascade_ct); + + if (is_persp) { + evsh->split[c-1] = LERP(lambda, linear_split, exp_split); + } + else { + evsh->split[c-1] = linear_split; + } + + /* NDC Space */ + float p[4] = {1.0f, 1.0f, evsh->split[c-1], 1.0f}; + mul_m4_v4(viewprojmat, p); + splits_ndc[c] = p[2]; + + if (is_persp) { + splits_ndc[c] /= p[3]; + } + } + /* For each cascade */ for (int c = 0; c < cascade_ct; ++c) { - float splitnear = LERP(((float)(c) / cascade_ct), -1.0f, 1.0f); - float splitfar = LERP(((float)(c + 1) / cascade_ct), -1.0f, 1.0f); - /* Given 8 frustrum corners */ float corners[8][4] = { - /* Far Cap */ - {-1.0f, -1.0f, splitfar, 1.0f}, - { 1.0f, -1.0f, splitfar, 1.0f}, - {-1.0f, 1.0f, splitfar, 1.0f}, - { 1.0f, 1.0f, splitfar, 1.0f}, /* Near Cap */ - {-1.0f, -1.0f, splitnear, 1.0f}, - { 1.0f, -1.0f, splitnear, 1.0f}, - {-1.0f, 1.0f, splitnear, 1.0f}, - { 1.0f, 1.0f, splitnear, 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_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} }; /* Transform them into world space */ - DRW_viewport_matrix_get(persmat, DRW_MAT_PERS); - invert_m4_m4(persinv, persmat); for (int i = 0; i < 8; ++i) { mul_m4_v4(persinv, corners[i]); mul_v3_fl(corners[i], 1.0f / corners[i][3]); @@ -351,21 +430,38 @@ static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE mul_m4_v4(viewmat, corners[i]); } - /* compute the minimum bounding box */ - INIT_MINMAX(minvec, maxvec); - for (int i = 0; i < 8; ++i) { - minmax_v3v3_v3(minvec, maxvec, corners[i]); - } + float center[3], radius; + frustum_min_bounding_sphere(corners, center, &radius); + + /* Snap projection center to nearest texel to cancel shimering. */ + float shadow_origin[2], shadow_texco[2]; + mul_v2_v2fl(shadow_origin, center, shadow_res / (2.0f * radius)); /* Light to texture space. */ + + /* Find the nearest texel. */ + shadow_texco[0] = round(shadow_origin[0]); + shadow_texco[1] = round(shadow_origin[1]); + + /* Compute offset. */ + sub_v2_v2(shadow_texco, shadow_origin); + mul_v2_fl(shadow_texco, (2.0f * radius) / shadow_res); /* Texture to light space. */ - /* expand the bounding box to cover light range */ - orthographic_m4(projmat, minvec[0], maxvec[0], minvec[1], maxvec[1], la->clipsta, la->clipend); + /* Apply offset. */ + add_v2_v2(center, shadow_texco); + + /* Expand the projection to cover frustum range */ + orthographic_m4(projmat, + center[0] - radius, + center[0] + radius, + center[1] - radius, + center[1] + radius, + la->clipsta, la->clipend); mul_m4_m4m4(evscp->viewprojmat[c], projmat, viewmat); mul_m4_m4m4(evsh->shadowmat[c], texcomat, evscp->viewprojmat[c]); - } - evsh->bias = 0.005f * la->bias; - evsh->count = (float)cascade_ct; + /* TODO modify bias depending on the cascade radius */ + evsh->bias[c] = 0.005f * la->bias; + } evli->shadowid = (float)(MAX_SHADOW_CUBE + MAX_SHADOW_MAP + evscp->shadow_id); } |