Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/engines/eevee/eevee_shadows_cascade.c')
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows_cascade.c439
1 files changed, 439 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
new file mode 100644
index 00000000000..b2dc8103df2
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
@@ -0,0 +1,439 @@
+/*
+ * 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 "BLI_rect.h"
+#include "BLI_sys_types.h" /* bool */
+
+#include "BKE_object.h"
+
+#include "eevee_private.h"
+
+#include "BLI_rand.h" /* needs to be after for some reason. */
+
+void EEVEE_shadows_cascade_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob)
+{
+ if (linfo->cascade_len >= MAX_SHADOW_CASCADE) {
+ return;
+ }
+
+ const Light *la = (Light *)ob->data;
+ EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len;
+ EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + linfo->cascade_len;
+ EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + linfo->cascade_len;
+
+ sh_data->bias = max_ff(la->bias * 0.00002f, 0.0f);
+ eevee_contact_shadow_setup(la, sh_data);
+
+ linfo->shadow_cascade_light_indices[linfo->cascade_len] = linfo->num_light;
+ evli->shadow_id = linfo->shadow_len++;
+ sh_data->type_data_id = linfo->cascade_len++;
+ csm_data->tex_id = linfo->num_cascade_layer;
+ csm_render->cascade_fade = la->cascade_fade;
+ csm_render->cascade_count = la->cascade_count;
+ csm_render->cascade_exponent = la->cascade_exponent;
+ csm_render->cascade_max_dist = la->cascade_max_dist;
+
+ linfo->num_cascade_layer += la->cascade_count;
+}
+
+static void shadow_cascade_random_matrix_set(float mat[4][4], float radius, int sample_ofs)
+{
+ float jitter[3];
+#ifndef DEBUG_SHADOW_DISTRIBUTION
+ EEVEE_sample_ellipse(sample_ofs, mat[0], mat[1], radius, radius, jitter);
+#else
+ for (int i = 0; i <= sample_ofs; i++) {
+ EEVEE_sample_ellipse(i, mat[0], mat[1], radius, radius, jitter);
+ float p[3];
+ add_v3_v3v3(p, jitter, mat[2]);
+ DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f});
+ }
+#endif
+ add_v3_v3(mat[2], jitter);
+ orthogonalize_m4(mat, 2);
+}
+
+static double round_to_digits(double value, int digits)
+{
+ double factor = pow(10.0, digits - ceil(log10(fabs(value))));
+ return round(value * factor) / factor;
+}
+
+static void frustum_min_bounding_sphere(const float corners[8][3],
+ float r_center[3],
+ float *r_radius)
+{
+#if 0 /* Simple solution but waste 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
+ /* Find averaged center. */
+ zero_v3(r_center);
+ for (int i = 0; i < 8; i++) {
+ add_v3_v3(r_center, corners[i]);
+ }
+ mul_v3_fl(r_center, 1.0f / 8.0f);
+
+ /* Search the largest distance from the sphere center. */
+ *r_radius = 0.0f;
+ for (int i = 0; i < 8; i++) {
+ float rad = len_squared_v3v3(corners[i], r_center);
+ if (rad > *r_radius) {
+ *r_radius = rad;
+ }
+ }
+
+ /* TODO try to reduce the radius further by moving the center.
+ * Remember we need a __stable__ solution! */
+
+ /* Try to reduce float imprecision leading to shimmering. */
+ *r_radius = (float)round_to_digits(sqrtf(*r_radius), 3);
+#endif
+}
+
+BLI_INLINE float lerp(float t, float a, float b)
+{
+ return ((a) + (t) * ((b) - (a)));
+}
+
+static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo,
+ EEVEE_Light *evli,
+ DRWView *view,
+ float view_near,
+ float view_far,
+ int sample_ofs)
+{
+ EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
+ EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
+ EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
+ (int)shdw_data->type_data_id;
+ int cascade_nbr = csm_render->cascade_count;
+ float cascade_fade = csm_render->cascade_fade;
+ float cascade_max_dist = csm_render->cascade_max_dist;
+ float cascade_exponent = csm_render->cascade_exponent;
+
+ float jitter_ofs[2];
+ double ht_point[2];
+ double ht_offset[2] = {0.0, 0.0};
+ uint ht_primes[2] = {2, 3};
+
+ BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
+
+ /* Not really sure why we need 4.0 factor here. */
+ jitter_ofs[0] = (ht_point[0] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
+ jitter_ofs[1] = (ht_point[1] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
+
+ /* Camera Matrices */
+ float persinv[4][4], vp_projmat[4][4];
+ DRW_view_persmat_get(view, persinv, true);
+ DRW_view_winmat_get(view, vp_projmat, false);
+ bool is_persp = DRW_view_is_persp_get(view);
+
+ /* obmat = Object Space > World Space */
+ /* viewmat = World Space > View Space */
+ float(*viewmat)[4] = csm_render->viewmat;
+ eevee_light_matrix_get(evli, viewmat);
+ /* At this point, viewmat == normalize_m4(obmat) */
+
+ if (linfo->soft_shadows) {
+ shadow_cascade_random_matrix_set(viewmat, evli->radius, sample_ofs);
+ }
+
+ copy_m4_m4(csm_render->viewinv, viewmat);
+ invert_m4(viewmat);
+
+ copy_v3_v3(csm_data->shadow_vec, csm_render->viewinv[2]);
+
+ /* Compute near and far value based on all shadow casters cumulated AABBs. */
+ float sh_near = -1.0e30f, sh_far = 1.0e30f;
+ BoundBox shcaster_bounds;
+ BKE_boundbox_init_from_minmax(
+ &shcaster_bounds, linfo->shcaster_aabb.min, linfo->shcaster_aabb.max);
+#ifdef DEBUG_CSM
+ float dbg_col1[4] = {1.0f, 0.5f, 0.6f, 1.0f};
+ DRW_debug_bbox(&shcaster_bounds, dbg_col1);
+#endif
+ for (int i = 0; i < 8; i++) {
+ mul_m4_v3(viewmat, shcaster_bounds.vec[i]);
+ sh_near = max_ff(sh_near, shcaster_bounds.vec[i][2]);
+ sh_far = min_ff(sh_far, shcaster_bounds.vec[i][2]);
+ }
+#ifdef DEBUG_CSM
+ float dbg_col2[4] = {0.5f, 1.0f, 0.6f, 1.0f};
+ float pts[2][3] = {{0.0, 0.0, sh_near}, {0.0, 0.0, sh_far}};
+ mul_m4_v3(csm_render->viewinv, pts[0]);
+ mul_m4_v3(csm_render->viewinv, pts[1]);
+ DRW_debug_sphere(pts[0], 1.0f, dbg_col1);
+ DRW_debug_sphere(pts[1], 1.0f, dbg_col2);
+#endif
+ /* The rest of the function is assuming inverted Z. */
+ /* Add a little bias to avoid invalid matrices. */
+ sh_far = -(sh_far - 1e-3);
+ sh_near = -sh_near;
+
+ /* 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, -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++) {
+ csm_data->split_start[c] = csm_end;
+ csm_data->split_end[c] = csm_end;
+ }
+
+ /* Compute split planes */
+ 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(vp_projmat, 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(vp_projmat, p);
+ splits_end_ndc[cascade_nbr - 1] = p[2];
+ if (is_persp) {
+ splits_end_ndc[cascade_nbr - 1] /= p[3];
+ }
+ }
+
+ csm_data->split_start[0] = csm_start;
+ csm_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), csm_start, csm_end);
+ float exp_split = csm_start * powf(csm_end / csm_start, (float)(c) / (float)cascade_nbr);
+
+ if (is_persp) {
+ csm_data->split_start[c] = lerp(cascade_exponent, linear_split, exp_split);
+ }
+ else {
+ csm_data->split_start[c] = linear_split;
+ }
+ csm_data->split_end[c - 1] = csm_data->split_start[c];
+
+ /* Add some overlap for smooth transition */
+ csm_data->split_start[c] = lerp(cascade_fade,
+ csm_data->split_end[c - 1],
+ (c > 1) ? csm_data->split_end[c - 2] :
+ csm_data->split_start[0]);
+
+ /* NDC Space */
+ {
+ float p[4] = {1.0f, 1.0f, csm_data->split_start[c], 1.0f};
+ /* TODO: we don't need full m4 multiply here */
+ mul_m4_v4(vp_projmat, p);
+ splits_start_ndc[c] = p[2];
+
+ if (is_persp) {
+ splits_start_ndc[c] /= p[3];
+ }
+ }
+
+ {
+ float p[4] = {1.0f, 1.0f, csm_data->split_end[c - 1], 1.0f};
+ /* TODO: we don't need full m4 multiply here */
+ mul_m4_v4(vp_projmat, 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) ? csm_data->split_end[cascade_nbr - 2] :
+ csm_data->split_start[0];
+ csm_data->split_start[0] = lerp(cascade_fade, csm_data->split_end[cascade_nbr - 1], prev_split);
+
+ /* For each cascade */
+ for (int c = 0; c < cascade_nbr; c++) {
+ float(*projmat)[4] = csm_render->projmat[c];
+ /* Given 8 frustum corners */
+ float corners[8][3] = {
+ /* Near Cap */
+ {1.0f, -1.0f, splits_start_ndc[c]},
+ {-1.0f, -1.0f, splits_start_ndc[c]},
+ {-1.0f, 1.0f, splits_start_ndc[c]},
+ {1.0f, 1.0f, splits_start_ndc[c]},
+ /* Far Cap */
+ {1.0f, -1.0f, splits_end_ndc[c]},
+ {-1.0f, -1.0f, splits_end_ndc[c]},
+ {-1.0f, 1.0f, splits_end_ndc[c]},
+ {1.0f, 1.0f, splits_end_ndc[c]},
+ };
+
+ /* Transform them into world space */
+ for (int i = 0; i < 8; i++) {
+ mul_project_m4_v3(persinv, corners[i]);
+ }
+
+ float center[3];
+ frustum_min_bounding_sphere(corners, center, &(csm_render->radius[c]));
+
+#ifdef DEBUG_CSM
+ float dbg_col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ if (c < 3) {
+ dbg_col[c] = 1.0f;
+ }
+ DRW_debug_bbox((BoundBox *)&corners, dbg_col);
+ DRW_debug_sphere(center, csm_render->radius[c], dbg_col);
+#endif
+
+ /* Project into lightspace */
+ mul_m4_v3(viewmat, center);
+
+ /* Snap projection center to nearest texel to cancel shimmering. */
+ float shadow_origin[2], shadow_texco[2];
+ /* Light to texture space. */
+ mul_v2_v2fl(
+ shadow_origin, center, linfo->shadow_cascade_size / (2.0f * csm_render->radius[c]));
+
+ /* Find the nearest texel. */
+ shadow_texco[0] = roundf(shadow_origin[0]);
+ shadow_texco[1] = roundf(shadow_origin[1]);
+
+ /* Compute offset. */
+ sub_v2_v2(shadow_texco, shadow_origin);
+ /* Texture to light space. */
+ mul_v2_fl(shadow_texco, (2.0f * csm_render->radius[c]) / linfo->shadow_cascade_size);
+
+ /* Apply offset. */
+ add_v2_v2(center, shadow_texco);
+
+ /* Expand the projection to cover frustum range */
+ rctf rect_cascade;
+ BLI_rctf_init_pt_radius(&rect_cascade, center, csm_render->radius[c]);
+ orthographic_m4(projmat,
+ rect_cascade.xmin,
+ rect_cascade.xmax,
+ rect_cascade.ymin,
+ rect_cascade.ymax,
+ sh_near,
+ sh_far);
+
+ /* Anti-Aliasing */
+ if (linfo->soft_shadows) {
+ add_v2_v2(projmat[3], jitter_ofs);
+ }
+
+ float viewprojmat[4][4];
+ mul_m4_m4m4(viewprojmat, projmat, viewmat);
+ mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat);
+
+#ifdef DEBUG_CSM
+ DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true);
+#endif
+ }
+
+ shdw_data->near = sh_near;
+ shdw_data->far = sh_far;
+}
+
+static void eevee_ensure_cascade_views(EEVEE_ShadowCascadeRender *csm_render,
+ DRWView *view[MAX_CASCADE_NUM])
+{
+ for (int i = 0; i < csm_render->cascade_count; i++) {
+ if (view[i] == NULL) {
+ view[i] = DRW_view_create(csm_render->viewmat, csm_render->projmat[i], NULL, NULL, NULL);
+ }
+ else {
+ DRW_view_update(view[i], csm_render->viewmat, csm_render->projmat[i], NULL, NULL);
+ }
+ }
+}
+
+void EEVEE_shadows_draw_cascades(EEVEE_ViewLayerData *sldata,
+ EEVEE_Data *vedata,
+ DRWView *view,
+ int cascade_index)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+ EEVEE_PrivateData *g_data = stl->g_data;
+ EEVEE_LightsInfo *linfo = sldata->lights;
+
+ EEVEE_Light *evli = linfo->light_data + linfo->shadow_cascade_light_indices[cascade_index];
+ EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
+ EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
+ EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
+ (int)shdw_data->type_data_id;
+
+ float near = DRW_view_near_distance_get(view);
+ float far = DRW_view_far_distance_get(view);
+
+ eevee_shadow_cascade_setup(linfo, evli, view, near, far, effects->taa_current_sample - 1);
+
+ /* Meh, Reusing the cube views. */
+ BLI_assert(MAX_CASCADE_NUM <= 6);
+ eevee_ensure_cascade_views(csm_render, g_data->cube_views);
+
+ /* Render shadow cascades */
+ /* Render cascade 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 < csm_render->cascade_count; j++) {
+ DRW_view_set_active(g_data->cube_views[j]);
+ int layer = csm_data->tex_id + j;
+ GPU_framebuffer_texture_layer_attach(
+ sldata->shadow_fb, sldata->shadow_cascade_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);
+ }
+}