diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2019-08-22 17:04:25 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2019-09-05 18:37:50 +0300 |
commit | d8aaf25c23fa10ee121dc4fdd1cafe544bcca355 (patch) | |
tree | b5a16157beac22e4b474c699c1ce25f3d8c5be51 /source/blender/draw/engines/eevee/eevee_shadows_cube.c | |
parent | ca58936f2ff2b14a649722be20d98f8fa35831ff (diff) |
Eevee: Shadow map refactor
Reviewed By: brecht
Differential Revision: http://developer.blender.org/D5659
Diffstat (limited to 'source/blender/draw/engines/eevee/eevee_shadows_cube.c')
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_shadows_cube.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cube.c b/source/blender/draw/engines/eevee/eevee_shadows_cube.c new file mode 100644 index 00000000000..b10c206703a --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shadows_cube.c @@ -0,0 +1,223 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2019, Blender Foundation. + */ + +/** \file + * \ingroup EEVEE + */ + +#include "eevee_private.h" + +void EEVEE_shadows_cube_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob) +{ + if (linfo->cube_len >= MAX_SHADOW_CUBE) { + return; + } + + const Light *la = (Light *)ob->data; + EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len; + + /* Always update dupli lights as EEVEE_LightEngineData is not saved. + * Same issue with dupli shadow casters. */ + bool update = (ob->base_flag & BASE_FROM_DUPLI) != 0; + if (!update) { + EEVEE_LightEngineData *led = EEVEE_light_data_ensure(ob); + if (led->need_update) { + update = true; + led->need_update = false; + } + } + + if (update) { + BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], linfo->cube_len); + } + + sh_data->near = max_ff(la->clipsta, 1e-8f); + sh_data->bias = max_ff(la->bias * 0.05f, 0.0f); + eevee_contact_shadow_setup(la, sh_data); + + /* Saving light bounds for later. */ + BoundSphere *cube_bound = linfo->shadow_bounds + linfo->cube_len; + copy_v3_v3(cube_bound->center, evli->position); + cube_bound->radius = sqrt(1.0f / evli->invsqrdist); + + linfo->shadow_cube_light_indices[linfo->cube_len] = linfo->num_light; + evli->shadow_id = linfo->shadow_len++; + sh_data->type_data_id = linfo->cube_len++; + + /* Same as linfo->cube_len, no need to save. */ + linfo->num_cube_layer++; +} + +static void shadow_cube_random_position_set(const EEVEE_Light *evli, + int sample_ofs, + float ws_sample_pos[3]) +{ + float jitter[3]; +#ifdef DEBUG_SHADOW_DISTRIBUTION + int i = 0; +start: +#else + int i = sample_ofs; +#endif + switch ((int)evli->light_type) { + case LA_AREA: + EEVEE_sample_rectangle(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter); + break; + case (int)LAMPTYPE_AREA_ELLIPSE: + EEVEE_sample_ellipse(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter); + break; + default: + EEVEE_sample_ball(i, evli->radius, jitter); + } +#ifdef DEBUG_SHADOW_DISTRIBUTION + float p[3]; + add_v3_v3v3(p, jitter, ws_sample_pos); + DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f}); + if (i++ < sample_ofs) { + goto start; + } +#endif + add_v3_v3(ws_sample_pos, jitter); +} + +/* Return true if sample has changed and light needs to be updated. */ +bool EEVEE_shadows_cube_setup(EEVEE_LightsInfo *linfo, const EEVEE_Light *evli, int sample_ofs) +{ + EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id; + EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id; + + eevee_light_matrix_get(evli, cube_data->shadowmat); + + shdw_data->far = max_ff(sqrt(1.0f / evli->invsqrdist), 3e-4); + shdw_data->near = min_ff(shdw_data->near, shdw_data->far - 1e-4); + + bool update = false; + + if (linfo->soft_shadows) { + shadow_cube_random_position_set(evli, sample_ofs, cube_data->shadowmat[3]); + /* Update if position changes (avoid infinite update if soft shadows does not move). + * Other changes are caught by depsgraph tagging. This one is for update between samples. */ + update = !compare_v3v3(cube_data->shadowmat[3], cube_data->position, 1e-10f); + /** + * Anti-Aliasing jitter: Add random rotation. + * + * The 2.0 factor is because texel angular size is not even across the cubemap, + * so we make the rotation range a bit bigger. + * This will not blur the shadow even if the spread is too big since we are just + * rotating the shadow cubemap. + * Note that this may be a rough approximation an may not converge to a perfectly + * smooth shadow (because sample distribution is quite non-uniform) but is enought + * in practice. + **/ + /* NOTE: this has implication for spotlight rendering optimization + * (see EEVEE_shadows_draw_cubemap). */ + float angular_texel_size = 2.0f * DEG2RADF(90) / (float)linfo->shadow_cube_size; + EEVEE_random_rotation_m4(sample_ofs, angular_texel_size, cube_data->shadowmat); + } + + copy_v3_v3(cube_data->position, cube_data->shadowmat[3]); + invert_m4(cube_data->shadowmat); + + return update; +} + +static void eevee_ensure_cube_views( + float near, float far, int cube_res, const float viewmat[4][4], DRWView *view[6]) +{ + float winmat[4][4]; + float side = near; + + /* TODO shadowcube array. */ + if (true) { + /* This half texel offset is used to ensure correct filtering between faces. */ + /* FIXME: This exhibit float precision issue with lower cube_res. + * But it seems to be caused by the perspective_m4. */ + side *= ((float)cube_res + 1.0f) / (float)(cube_res); + } + + perspective_m4(winmat, -side, side, -side, side, near, far); + + for (int i = 0; i < 6; i++) { + float tmp[4][4]; + mul_m4_m4m4(tmp, cubefacemat[i], viewmat); + + if (view[i] == NULL) { + view[i] = DRW_view_create(tmp, winmat, NULL, NULL, NULL); + } + else { + DRW_view_update(view[i], tmp, winmat, NULL, NULL); + } + } +} + +/* Does a spot angle fits a single cubeface. */ +static bool spot_angle_fit_single_face(const EEVEE_Light *evli) +{ + /* alpha = spot/cone half angle. */ + /* beta = scaled spot/cone half angle. */ + float cos_alpha = evli->spotsize; + float sin_alpha = sqrtf(max_ff(0.0f, 1.0f - cos_alpha * cos_alpha)); + float cos_beta = min_ff(cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizex), + cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizey)); + /* Don't use 45 degrees because AA jitter can offset the face. */ + return cos_beta > cosf(DEG2RADF(42.0f)); +} + +void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int cube_index) +{ + EEVEE_PassList *psl = vedata->psl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_PrivateData *g_data = stl->g_data; + EEVEE_LightsInfo *linfo = sldata->lights; + + EEVEE_Light *evli = linfo->light_data + linfo->shadow_cube_light_indices[cube_index]; + EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id; + EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id; + + eevee_ensure_cube_views(shdw_data->near, + shdw_data->far, + linfo->shadow_cube_size, + cube_data->shadowmat, + g_data->cube_views); + + /* Render shadow cube */ + /* Render 6 faces separately: seems to be faster for the general case. + * The only time it's more beneficial is when the CPU culling overhead + * outweigh the instancing overhead. which is rarely the case. */ + for (int j = 0; j < 6; j++) { + /* Optimization: Only render the needed faces. */ + /* Skip all but -Z face. */ + if (evli->light_type == LA_SPOT && j != 5 && spot_angle_fit_single_face(evli)) + continue; + /* Skip +Z face. */ + if (evli->light_type != LA_LOCAL && j == 4) + continue; + /* TODO(fclem) some cube sides can be invisible in the main views. Cull them. */ + // if (frustum_intersect(g_data->cube_views[j], main_view)) + // continue; + + DRW_view_set_active(g_data->cube_views[j]); + int layer = cube_index * 6 + j; + GPU_framebuffer_texture_layer_attach(sldata->shadow_fb, sldata->shadow_cube_pool, 0, layer, 0); + GPU_framebuffer_bind(sldata->shadow_fb); + GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f); + DRW_draw_pass(psl->shadow_pass); + } + + BLI_BITMAP_SET(&linfo->sh_cube_update[0], cube_index, false); +}
\ No newline at end of file |