This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/draw/engines/eevee/eevee_shadows_cascade.c

438 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
}
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};
const 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 = interpf(csm_end, csm_start, c / (float)cascade_nbr);
float exp_split = csm_start * powf(csm_end / csm_start, c / (float)cascade_nbr);
if (is_persp) {
csm_data->split_start[c] = interpf(exp_split, linear_split, cascade_exponent);
}
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] = interpf((c > 1) ? csm_data->split_end[c - 2] :
csm_data->split_start[0],
csm_data->split_end[c - 1],
cascade_fade);
/* 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] = interpf(
prev_split, csm_data->split_end[cascade_nbr - 1], cascade_fade);
/* 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 light-space. */
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 clip-space, 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);
}
}