This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
Clément Foucault 31ad86884c EEVEE: Shadow: Fix cascade shadowmap bias multiplicator
This fixes the issue where sun shadowmaps needs a very big bias value to
make any difference.

The bias is now in world space and not dependant on shadow bounds.

Unfortunatelly this breaks compatibility with previous version and old
scene are likely to need user intervention to fix.

Also fixes the property range.

Fix T71661 EEVEE shadow from sun on incorrect face
2020-02-14 12:26:49 +01:00

442 lines
14 KiB
C

/*
* 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;
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;
csm_render->original_bias = max_ff(la->bias, 0.0f);
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
}
/* Bias is in clipspace, divide by range. */
shdw_data->bias = csm_render->original_bias * 0.05f / fabsf(sh_far - sh_near);
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);
}
}