2017-03-17 00:00:46 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright 2016, Blender Foundation.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s): Blender Institute
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/** \file eevee_lights.c
|
|
|
|
|
* \ingroup DNA
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "DRW_render.h"
|
|
|
|
|
|
2017-09-08 20:21:57 +02:00
|
|
|
#include "BLI_dynstr.h"
|
2018-03-02 18:35:25 +01:00
|
|
|
#include "BLI_rect.h"
|
2017-09-08 20:21:57 +02:00
|
|
|
|
2017-06-01 18:20:44 +02:00
|
|
|
#include "BKE_object.h"
|
|
|
|
|
|
2017-04-26 17:42:39 +10:00
|
|
|
#include "eevee_engine.h"
|
2017-03-17 00:00:46 +01:00
|
|
|
#include "eevee_private.h"
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
#define SHADOW_CASTER_ALLOC_CHUNK 16
|
2017-06-01 18:20:44 +02:00
|
|
|
|
2017-05-20 13:16:14 +02:00
|
|
|
static struct {
|
|
|
|
|
struct GPUShader *shadow_sh;
|
2017-09-01 18:39:39 +02:00
|
|
|
struct GPUShader *shadow_store_cube_sh[SHADOW_METHOD_MAX];
|
|
|
|
|
struct GPUShader *shadow_store_cascade_sh[SHADOW_METHOD_MAX];
|
2017-09-08 20:21:57 +02:00
|
|
|
struct GPUShader *shadow_copy_cube_sh[SHADOW_METHOD_MAX];
|
|
|
|
|
struct GPUShader *shadow_copy_cascade_sh[SHADOW_METHOD_MAX];
|
2017-05-20 13:16:14 +02:00
|
|
|
} e_data = {NULL}; /* Engine data */
|
|
|
|
|
|
|
|
|
|
extern char datatoc_shadow_vert_glsl[];
|
|
|
|
|
extern char datatoc_shadow_frag_glsl[];
|
|
|
|
|
extern char datatoc_shadow_store_frag_glsl[];
|
2017-09-08 20:21:57 +02:00
|
|
|
extern char datatoc_shadow_copy_frag_glsl[];
|
|
|
|
|
extern char datatoc_concentric_samples_lib_glsl[];
|
2017-05-20 13:16:14 +02:00
|
|
|
|
2018-01-09 18:01:56 +01:00
|
|
|
/* Prototype */
|
|
|
|
|
static void eevee_light_setup(Object *ob, EEVEE_Light *evli);
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* *********** LIGHT BITS *********** */
|
|
|
|
|
static void lightbits_set_single(EEVEE_LightBits *bitf, unsigned int idx, bool val)
|
|
|
|
|
{
|
|
|
|
|
if (val) {
|
|
|
|
|
bitf->fields[idx / 8] |= (1 << (idx % 8));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bitf->fields[idx / 8] &= ~(1 << (idx % 8));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void lightbits_set_all(EEVEE_LightBits *bitf, bool val)
|
|
|
|
|
{
|
|
|
|
|
memset(bitf, (val) ? 0xFF : 0x00, sizeof(EEVEE_LightBits));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void lightbits_or(EEVEE_LightBits *r, const EEVEE_LightBits *v)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < MAX_LIGHTBITS_FIELDS; ++i) {
|
|
|
|
|
r->fields[i] |= v->fields[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool lightbits_get(const EEVEE_LightBits *r, unsigned int idx)
|
|
|
|
|
{
|
|
|
|
|
return r->fields[idx / 8] & (1 << (idx % 8));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void lightbits_convert(EEVEE_LightBits *r, const EEVEE_LightBits *bitf, const int *light_bit_conv_table, unsigned int table_length)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < table_length; ++i) {
|
|
|
|
|
if (lightbits_get(bitf, i) != 0) {
|
|
|
|
|
if (light_bit_conv_table[i] >= 0) {
|
|
|
|
|
r->fields[i / 8] |= (1 << (i % 8));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* *********** FUNCTIONS *********** */
|
2017-03-17 00:00:46 +01:00
|
|
|
|
2017-11-22 10:52:39 -02:00
|
|
|
void EEVEE_lights_init(EEVEE_ViewLayerData *sldata)
|
2017-03-17 00:00:46 +01:00
|
|
|
{
|
2017-09-05 21:02:17 +02:00
|
|
|
const unsigned int shadow_ubo_size = sizeof(EEVEE_Shadow) * MAX_SHADOW +
|
|
|
|
|
sizeof(EEVEE_ShadowCube) * MAX_SHADOW_CUBE +
|
2017-04-10 12:06:17 +02:00
|
|
|
sizeof(EEVEE_ShadowCascade) * MAX_SHADOW_CASCADE;
|
|
|
|
|
|
2017-09-01 15:59:01 +02:00
|
|
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
2017-11-22 10:52:39 -02:00
|
|
|
ViewLayer *view_layer = draw_ctx->view_layer;
|
|
|
|
|
IDProperty *props = BKE_view_layer_engine_evaluated_get(view_layer, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_EEVEE);
|
2017-09-01 15:59:01 +02:00
|
|
|
|
2017-05-20 13:16:14 +02:00
|
|
|
if (!e_data.shadow_sh) {
|
|
|
|
|
e_data.shadow_sh = DRW_shader_create(
|
2018-04-15 22:22:50 +02:00
|
|
|
datatoc_shadow_vert_glsl, NULL, datatoc_shadow_frag_glsl, NULL);
|
2017-05-20 13:16:14 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-30 22:29:20 +02:00
|
|
|
if (!sldata->lamps) {
|
|
|
|
|
sldata->lamps = MEM_callocN(sizeof(EEVEE_LampsInfo), "EEVEE_LampsInfo");
|
|
|
|
|
sldata->light_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_Light) * MAX_LIGHT, NULL);
|
|
|
|
|
sldata->shadow_ubo = DRW_uniformbuffer_create(shadow_ubo_size, NULL);
|
|
|
|
|
sldata->shadow_render_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_ShadowRender), NULL);
|
2018-01-11 14:08:21 +01:00
|
|
|
|
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
|
|
|
sldata->shcasters_buffers[i].shadow_casters = MEM_callocN(sizeof(EEVEE_ShadowCaster) * SHADOW_CASTER_ALLOC_CHUNK, "EEVEE_ShadowCaster buf");
|
|
|
|
|
sldata->shcasters_buffers[i].flags = MEM_callocN(sizeof(sldata->shcasters_buffers[0].flags) * SHADOW_CASTER_ALLOC_CHUNK, "EEVEE_shcast_buffer flags buf");
|
|
|
|
|
sldata->shcasters_buffers[i].alloc_count = SHADOW_CASTER_ALLOC_CHUNK;
|
|
|
|
|
sldata->shcasters_buffers[i].count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sldata->lamps->shcaster_frontbuffer = &sldata->shcasters_buffers[0];
|
|
|
|
|
sldata->lamps->shcaster_backbuffer = &sldata->shcasters_buffers[1];
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-09-01 15:59:01 +02:00
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Flip buffers */
|
|
|
|
|
SWAP(EEVEE_ShadowCasterBuffer *, sldata->lamps->shcaster_frontbuffer, sldata->lamps->shcaster_backbuffer);
|
|
|
|
|
|
2017-09-01 15:59:01 +02:00
|
|
|
int sh_method = BKE_collection_engine_property_value_get_int(props, "shadow_method");
|
|
|
|
|
int sh_size = BKE_collection_engine_property_value_get_int(props, "shadow_size");
|
2017-09-02 02:27:28 +02:00
|
|
|
int sh_high_bitdepth = BKE_collection_engine_property_value_get_int(props, "shadow_high_bitdepth");
|
2017-09-01 15:59:01 +02:00
|
|
|
|
|
|
|
|
EEVEE_LampsInfo *linfo = sldata->lamps;
|
2017-09-02 02:27:28 +02:00
|
|
|
if ((linfo->shadow_size != sh_size) ||
|
2017-10-07 15:57:14 +11:00
|
|
|
(linfo->shadow_method != sh_method) ||
|
|
|
|
|
(linfo->shadow_high_bitdepth != sh_high_bitdepth))
|
2017-09-02 02:27:28 +02:00
|
|
|
{
|
2017-09-01 15:59:01 +02:00
|
|
|
BLI_assert((sh_size > 0) && (sh_size <= 8192));
|
|
|
|
|
DRW_TEXTURE_FREE_SAFE(sldata->shadow_pool);
|
2017-09-02 02:27:28 +02:00
|
|
|
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_target);
|
2017-09-01 15:59:01 +02:00
|
|
|
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_target);
|
2017-09-09 21:11:22 +02:00
|
|
|
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_blur);
|
|
|
|
|
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_blur);
|
2017-09-01 15:59:01 +02:00
|
|
|
|
2017-09-02 02:27:28 +02:00
|
|
|
linfo->shadow_high_bitdepth = sh_high_bitdepth;
|
2017-09-01 18:39:39 +02:00
|
|
|
linfo->shadow_method = sh_method;
|
2017-09-01 15:59:01 +02:00
|
|
|
linfo->shadow_size = sh_size;
|
|
|
|
|
linfo->shadow_render_data.stored_texel_size = 1.0 / (float)linfo->shadow_size;
|
|
|
|
|
|
|
|
|
|
/* Compute adequate size for the cubemap render target.
|
|
|
|
|
* The 3.0f factor is here to make sure there is no under sampling between
|
|
|
|
|
* the octahedron mapping and the cubemap. */
|
|
|
|
|
int new_cube_target_size = (int)ceil(sqrt((float)(sh_size * sh_size) / 6.0f) * 3.0f);
|
|
|
|
|
|
|
|
|
|
CLAMP(new_cube_target_size, 1, 4096);
|
|
|
|
|
|
2017-09-02 02:27:28 +02:00
|
|
|
linfo->shadow_cube_target_size = new_cube_target_size;
|
|
|
|
|
linfo->shadow_render_data.cube_texel_size = 1.0 / (float)linfo->shadow_cube_target_size;
|
2017-09-01 15:59:01 +02:00
|
|
|
}
|
2018-04-08 14:31:41 +02:00
|
|
|
|
|
|
|
|
/* only compile the ones needed. reduce startup time. */
|
|
|
|
|
if ((sh_method == SHADOW_ESM) && !e_data.shadow_store_cube_sh[SHADOW_ESM]) {
|
|
|
|
|
DynStr *ds_frag = BLI_dynstr_new();
|
|
|
|
|
BLI_dynstr_append(ds_frag, datatoc_concentric_samples_lib_glsl);
|
|
|
|
|
BLI_dynstr_append(ds_frag, datatoc_shadow_store_frag_glsl);
|
|
|
|
|
char *store_shadow_shader_str = BLI_dynstr_get_cstring(ds_frag);
|
|
|
|
|
BLI_dynstr_free(ds_frag);
|
|
|
|
|
|
|
|
|
|
e_data.shadow_store_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
|
|
|
|
|
store_shadow_shader_str,
|
|
|
|
|
"#define ESM\n");
|
|
|
|
|
e_data.shadow_store_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
|
|
|
|
|
store_shadow_shader_str,
|
|
|
|
|
"#define ESM\n"
|
|
|
|
|
"#define CSM\n");
|
|
|
|
|
MEM_freeN(store_shadow_shader_str);
|
|
|
|
|
|
|
|
|
|
e_data.shadow_copy_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
|
|
|
|
|
datatoc_shadow_copy_frag_glsl,
|
|
|
|
|
"#define ESM\n"
|
|
|
|
|
"#define COPY\n");
|
|
|
|
|
e_data.shadow_copy_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
|
|
|
|
|
datatoc_shadow_copy_frag_glsl,
|
|
|
|
|
"#define ESM\n"
|
|
|
|
|
"#define COPY\n"
|
|
|
|
|
"#define CSM\n");
|
|
|
|
|
}
|
|
|
|
|
else if ((sh_method == SHADOW_VSM) && !e_data.shadow_store_cube_sh[SHADOW_VSM]) {
|
|
|
|
|
DynStr *ds_frag = BLI_dynstr_new();
|
|
|
|
|
BLI_dynstr_append(ds_frag, datatoc_concentric_samples_lib_glsl);
|
|
|
|
|
BLI_dynstr_append(ds_frag, datatoc_shadow_store_frag_glsl);
|
|
|
|
|
char *store_shadow_shader_str = BLI_dynstr_get_cstring(ds_frag);
|
|
|
|
|
BLI_dynstr_free(ds_frag);
|
|
|
|
|
|
|
|
|
|
e_data.shadow_store_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
|
|
|
|
|
store_shadow_shader_str,
|
|
|
|
|
"#define VSM\n");
|
|
|
|
|
e_data.shadow_store_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
|
|
|
|
|
store_shadow_shader_str,
|
|
|
|
|
"#define VSM\n"
|
|
|
|
|
"#define CSM\n");
|
|
|
|
|
MEM_freeN(store_shadow_shader_str);
|
|
|
|
|
|
|
|
|
|
e_data.shadow_copy_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
|
|
|
|
|
datatoc_shadow_copy_frag_glsl,
|
|
|
|
|
"#define VSM\n"
|
|
|
|
|
"#define COPY\n");
|
|
|
|
|
e_data.shadow_copy_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
|
|
|
|
|
datatoc_shadow_copy_frag_glsl,
|
|
|
|
|
"#define VSM\n"
|
|
|
|
|
"#define COPY\n"
|
|
|
|
|
"#define CSM\n");
|
|
|
|
|
}
|
2017-03-17 00:00:46 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-01 19:27:38 +01:00
|
|
|
void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
2017-03-17 00:00:46 +01:00
|
|
|
{
|
2017-05-30 22:29:20 +02:00
|
|
|
EEVEE_LampsInfo *linfo = sldata->lamps;
|
2018-03-01 19:27:38 +01:00
|
|
|
EEVEE_StorageList *stl = vedata->stl;
|
|
|
|
|
EEVEE_PassList *psl = vedata->psl;
|
2017-03-17 00:00:46 +01:00
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
linfo->shcaster_frontbuffer->count = 0;
|
2017-09-05 21:02:17 +02:00
|
|
|
linfo->num_light = 0;
|
|
|
|
|
linfo->num_layer = 0;
|
|
|
|
|
linfo->gpu_cube_ct = linfo->gpu_cascade_ct = linfo->gpu_shadow_ct = 0;
|
|
|
|
|
linfo->cpu_cube_ct = linfo->cpu_cascade_ct = 0;
|
2017-04-10 12:06:17 +02:00
|
|
|
memset(linfo->light_ref, 0, sizeof(linfo->light_ref));
|
|
|
|
|
memset(linfo->shadow_cube_ref, 0, sizeof(linfo->shadow_cube_ref));
|
|
|
|
|
memset(linfo->shadow_cascade_ref, 0, sizeof(linfo->shadow_cascade_ref));
|
2018-01-11 14:08:21 +01:00
|
|
|
memset(linfo->new_shadow_id, -1, sizeof(linfo->new_shadow_id));
|
|
|
|
|
|
|
|
|
|
/* Shadow Casters: Reset flags. */
|
|
|
|
|
memset(linfo->shcaster_backbuffer->flags, (char)SHADOW_CASTER_PRUNED, linfo->shcaster_backbuffer->alloc_count);
|
|
|
|
|
memset(linfo->shcaster_frontbuffer->flags, 0x00, linfo->shcaster_frontbuffer->alloc_count);
|
2017-05-20 13:16:14 +02:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
psl->shadow_cube_store_pass = DRW_pass_create("Shadow Storage Pass", DRW_STATE_WRITE_COLOR);
|
|
|
|
|
|
2017-11-20 14:28:24 +11:00
|
|
|
DRWShadingGroup *grp = DRW_shgroup_create(
|
|
|
|
|
e_data.shadow_store_cube_sh[linfo->shadow_method], psl->shadow_cube_store_pass);
|
2018-03-25 19:24:19 +02:00
|
|
|
DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cube_blur);
|
2017-09-01 15:59:01 +02:00
|
|
|
DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
|
2017-09-05 21:02:17 +02:00
|
|
|
DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
|
2017-09-01 15:59:01 +02:00
|
|
|
DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
psl->shadow_cascade_store_pass = DRW_pass_create("Shadow Cascade Storage Pass", DRW_STATE_WRITE_COLOR);
|
|
|
|
|
|
2017-11-20 14:28:24 +11:00
|
|
|
DRWShadingGroup *grp = DRW_shgroup_create(
|
|
|
|
|
e_data.shadow_store_cascade_sh[linfo->shadow_method], psl->shadow_cascade_store_pass);
|
2018-03-25 19:24:19 +02:00
|
|
|
DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cascade_blur);
|
2017-05-30 22:29:20 +02:00
|
|
|
DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
|
2017-09-05 21:02:17 +02:00
|
|
|
DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
|
|
|
|
|
DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
|
2017-05-20 13:16:14 +02:00
|
|
|
DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-08 20:21:57 +02:00
|
|
|
{
|
|
|
|
|
psl->shadow_cube_copy_pass = DRW_pass_create("Shadow Copy Pass", DRW_STATE_WRITE_COLOR);
|
|
|
|
|
|
2017-11-20 14:28:24 +11:00
|
|
|
DRWShadingGroup *grp = DRW_shgroup_create(
|
|
|
|
|
e_data.shadow_copy_cube_sh[linfo->shadow_method], psl->shadow_cube_copy_pass);
|
2018-03-25 19:24:19 +02:00
|
|
|
DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cube_target);
|
2017-09-08 20:21:57 +02:00
|
|
|
DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
|
|
|
|
|
DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
|
|
|
|
|
DRW_shgroup_uniform_int(grp, "faceId", &linfo->current_shadow_face, 1);
|
|
|
|
|
DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
psl->shadow_cascade_copy_pass = DRW_pass_create("Shadow Cascade Copy Pass", DRW_STATE_WRITE_COLOR);
|
|
|
|
|
|
2017-11-20 14:28:24 +11:00
|
|
|
DRWShadingGroup *grp = DRW_shgroup_create(
|
|
|
|
|
e_data.shadow_copy_cascade_sh[linfo->shadow_method], psl->shadow_cascade_copy_pass);
|
2018-03-25 19:24:19 +02:00
|
|
|
DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cascade_target);
|
2017-09-08 20:21:57 +02:00
|
|
|
DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
|
|
|
|
|
DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
|
|
|
|
|
DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
|
|
|
|
|
DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-20 13:16:14 +02:00
|
|
|
{
|
2018-03-01 19:27:38 +01:00
|
|
|
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
|
|
|
|
|
psl->shadow_pass = DRW_pass_create("Shadow Pass", state);
|
2017-05-20 13:16:14 +02:00
|
|
|
|
2018-04-15 22:22:50 +02:00
|
|
|
stl->g_data->shadow_shgrp = DRW_shgroup_create(e_data.shadow_sh, psl->shadow_pass);
|
2017-05-20 13:16:14 +02:00
|
|
|
}
|
2017-03-17 00:00:46 +01:00
|
|
|
}
|
|
|
|
|
|
2017-11-22 10:52:39 -02:00
|
|
|
void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob)
|
2017-03-17 00:00:46 +01:00
|
|
|
{
|
2017-05-30 22:29:20 +02:00
|
|
|
EEVEE_LampsInfo *linfo = sldata->lamps;
|
2017-03-17 00:00:46 +01:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Step 1 find all lamps in the scene and setup them */
|
2017-09-12 13:13:20 +02:00
|
|
|
if (linfo->num_light >= MAX_LIGHT) {
|
2018-01-25 10:59:41 -02:00
|
|
|
printf("Too many lamps in the scene !!!\n");
|
2017-03-17 00:00:46 +01:00
|
|
|
}
|
2017-04-10 12:06:17 +02:00
|
|
|
else {
|
|
|
|
|
Lamp *la = (Lamp *)ob->data;
|
2018-01-09 18:01:56 +01:00
|
|
|
EEVEE_Light *evli = linfo->light_data + linfo->num_light;
|
|
|
|
|
eevee_light_setup(ob, evli);
|
|
|
|
|
|
|
|
|
|
/* We do not support shadowmaps for dupli lamps. */
|
|
|
|
|
if ((ob->base_flag & BASE_FROMDUPLI) != 0) {
|
|
|
|
|
linfo->num_light++;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-29 10:36:58 +01:00
|
|
|
EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
|
2017-06-01 18:20:44 +02:00
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Save previous shadow id. */
|
|
|
|
|
int prev_cube_sh_id = led->prev_cube_shadow_id;
|
|
|
|
|
|
|
|
|
|
/* Default light without shadows */
|
|
|
|
|
led->data.ld.shadow_id = -1;
|
|
|
|
|
led->prev_cube_shadow_id = -1;
|
2017-04-10 12:06:17 +02:00
|
|
|
|
2018-04-24 12:11:31 +02:00
|
|
|
if (la->mode & LA_SHADOW) {
|
2017-10-07 15:57:14 +11:00
|
|
|
if (la->type == LA_SUN) {
|
2017-09-05 21:02:17 +02:00
|
|
|
int sh_nbr = 1; /* TODO : MSM */
|
|
|
|
|
int cascade_nbr = MAX_CASCADE_NUM; /* TODO : Custom cascade number */
|
|
|
|
|
|
|
|
|
|
if ((linfo->gpu_cascade_ct + sh_nbr) <= MAX_SHADOW_CASCADE) {
|
|
|
|
|
/* Save Light object. */
|
|
|
|
|
linfo->shadow_cascade_ref[linfo->cpu_cascade_ct] = ob;
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Store indices. */
|
|
|
|
|
EEVEE_ShadowCascadeData *data = &led->data.scad;
|
2017-09-05 21:02:17 +02:00
|
|
|
data->shadow_id = linfo->gpu_shadow_ct;
|
|
|
|
|
data->cascade_id = linfo->gpu_cascade_ct;
|
|
|
|
|
data->layer_id = linfo->num_layer;
|
|
|
|
|
|
|
|
|
|
/* Increment indices. */
|
|
|
|
|
linfo->gpu_shadow_ct += 1;
|
|
|
|
|
linfo->gpu_cascade_ct += sh_nbr;
|
|
|
|
|
linfo->num_layer += sh_nbr * cascade_nbr;
|
|
|
|
|
|
|
|
|
|
linfo->cpu_cascade_ct += 1;
|
|
|
|
|
}
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-09-05 21:02:17 +02:00
|
|
|
else if (la->type == LA_SPOT || la->type == LA_LOCAL || la->type == LA_AREA) {
|
|
|
|
|
int sh_nbr = 1; /* TODO : MSM */
|
|
|
|
|
|
|
|
|
|
if ((linfo->gpu_cube_ct + sh_nbr) <= MAX_SHADOW_CUBE) {
|
|
|
|
|
/* Save Light object. */
|
|
|
|
|
linfo->shadow_cube_ref[linfo->cpu_cube_ct] = ob;
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* For light update tracking. */
|
|
|
|
|
if ((prev_cube_sh_id >= 0) &&
|
2018-02-15 18:03:55 +11:00
|
|
|
(prev_cube_sh_id < linfo->shcaster_backbuffer->count))
|
2018-01-11 14:08:21 +01:00
|
|
|
{
|
|
|
|
|
linfo->new_shadow_id[prev_cube_sh_id] = linfo->cpu_cube_ct;
|
|
|
|
|
}
|
|
|
|
|
led->prev_cube_shadow_id = linfo->cpu_cube_ct;
|
|
|
|
|
|
|
|
|
|
/* Saving lamp bounds for later. */
|
|
|
|
|
BLI_assert(linfo->cpu_cube_ct >= 0 && linfo->cpu_cube_ct < MAX_LIGHT);
|
|
|
|
|
copy_v3_v3(linfo->shadow_bounds[linfo->cpu_cube_ct].center, ob->obmat[3]);
|
|
|
|
|
linfo->shadow_bounds[linfo->cpu_cube_ct].radius = la->clipend;
|
|
|
|
|
|
|
|
|
|
EEVEE_ShadowCubeData *data = &led->data.scd;
|
|
|
|
|
/* Store indices. */
|
2017-09-05 21:02:17 +02:00
|
|
|
data->shadow_id = linfo->gpu_shadow_ct;
|
|
|
|
|
data->cube_id = linfo->gpu_cube_ct;
|
|
|
|
|
data->layer_id = linfo->num_layer;
|
|
|
|
|
|
|
|
|
|
/* Increment indices. */
|
|
|
|
|
linfo->gpu_shadow_ct += 1;
|
|
|
|
|
linfo->gpu_cube_ct += sh_nbr;
|
|
|
|
|
linfo->num_layer += sh_nbr;
|
|
|
|
|
|
|
|
|
|
linfo->cpu_cube_ct += 1;
|
|
|
|
|
}
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-03-17 00:00:46 +01:00
|
|
|
}
|
2017-09-05 21:02:17 +02:00
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
led->data.ld.light_id = linfo->num_light;
|
2017-04-10 12:06:17 +02:00
|
|
|
linfo->light_ref[linfo->num_light] = ob;
|
|
|
|
|
linfo->num_light++;
|
2017-03-17 00:00:46 +01:00
|
|
|
}
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-20 13:16:14 +02:00
|
|
|
/* Add a shadow caster to the shadowpasses */
|
2017-11-20 14:28:24 +11:00
|
|
|
void EEVEE_lights_cache_shcaster_add(
|
2018-04-15 22:22:50 +02:00
|
|
|
EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_StorageList *stl, struct Gwn_Batch *geom, Object *ob)
|
2017-05-20 13:16:14 +02:00
|
|
|
{
|
2018-04-15 22:22:50 +02:00
|
|
|
DRW_shgroup_call_object_add(
|
2018-03-01 19:27:38 +01:00
|
|
|
stl->g_data->shadow_shgrp,
|
2018-04-15 22:22:50 +02:00
|
|
|
geom, ob);
|
2017-05-20 13:16:14 +02:00
|
|
|
}
|
|
|
|
|
|
2017-07-11 01:10:57 +02:00
|
|
|
void EEVEE_lights_cache_shcaster_material_add(
|
2018-04-15 22:22:50 +02:00
|
|
|
EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_PassList *psl, struct GPUMaterial *gpumat,
|
|
|
|
|
struct Gwn_Batch *geom, struct Object *ob, float *alpha_threshold)
|
2017-07-11 01:10:57 +02:00
|
|
|
{
|
2018-03-01 19:27:38 +01:00
|
|
|
/* TODO / PERF : reuse the same shading group for objects with the same material */
|
|
|
|
|
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, psl->shadow_pass);
|
2017-07-11 12:42:59 +02:00
|
|
|
|
|
|
|
|
if (grp == NULL) return;
|
|
|
|
|
|
2017-07-11 01:10:57 +02:00
|
|
|
if (alpha_threshold != NULL)
|
|
|
|
|
DRW_shgroup_uniform_float(grp, "alphaThreshold", alpha_threshold, 1);
|
|
|
|
|
|
2018-04-15 22:22:50 +02:00
|
|
|
DRW_shgroup_call_object_add(grp, geom, ob);
|
2017-07-11 01:10:57 +02:00
|
|
|
}
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Make that object update shadow casting lamps inside its influence bounding box. */
|
|
|
|
|
void EEVEE_lights_cache_shcaster_object_add(EEVEE_ViewLayerData *sldata, Object *ob)
|
|
|
|
|
{
|
|
|
|
|
if ((ob->base_flag & BASE_FROMDUPLI) != 0) {
|
|
|
|
|
/* TODO: Special case for dupli objects because we cannot save the object pointer. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_ensure(ob);
|
|
|
|
|
EEVEE_LampsInfo *linfo = sldata->lamps;
|
|
|
|
|
EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer;
|
|
|
|
|
EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer;
|
|
|
|
|
int past_id = oedata->shadow_caster_id;
|
|
|
|
|
|
|
|
|
|
/* Update flags in backbuffer. */
|
|
|
|
|
if (past_id > -1 && past_id < backbuffer->count) {
|
|
|
|
|
backbuffer->flags[past_id] &= ~SHADOW_CASTER_PRUNED;
|
|
|
|
|
|
|
|
|
|
if (oedata->need_update) {
|
|
|
|
|
backbuffer->flags[past_id] |= SHADOW_CASTER_UPDATED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update id. */
|
|
|
|
|
oedata->shadow_caster_id = frontbuffer->count++;
|
|
|
|
|
|
|
|
|
|
/* Make sure shadow_casters is big enough. */
|
|
|
|
|
if (oedata->shadow_caster_id >= frontbuffer->alloc_count) {
|
|
|
|
|
frontbuffer->alloc_count += SHADOW_CASTER_ALLOC_CHUNK;
|
|
|
|
|
frontbuffer->shadow_casters = MEM_reallocN(frontbuffer->shadow_casters, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
|
|
|
|
|
frontbuffer->flags = MEM_reallocN(frontbuffer->flags, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EEVEE_ShadowCaster *shcaster = frontbuffer->shadow_casters + oedata->shadow_caster_id;
|
|
|
|
|
|
|
|
|
|
if (oedata->need_update) {
|
|
|
|
|
frontbuffer->flags[oedata->shadow_caster_id] = SHADOW_CASTER_UPDATED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update World AABB in frontbuffer. */
|
|
|
|
|
BoundBox *bb = BKE_object_boundbox_get(ob);
|
|
|
|
|
float min[3], max[3];
|
|
|
|
|
INIT_MINMAX(min, max);
|
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
|
float vec[3];
|
|
|
|
|
copy_v3_v3(vec, bb->vec[i]);
|
|
|
|
|
mul_m4_v3(ob->obmat, vec);
|
|
|
|
|
minmax_v3v3_v3(min, max, vec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EEVEE_BoundBox *aabb = &shcaster->bbox;
|
|
|
|
|
add_v3_v3v3(aabb->center, min, max);
|
|
|
|
|
mul_v3_fl(aabb->center, 0.5f);
|
|
|
|
|
sub_v3_v3v3(aabb->halfdim, aabb->center, max);
|
|
|
|
|
|
|
|
|
|
aabb->halfdim[0] = fabsf(aabb->halfdim[0]);
|
|
|
|
|
aabb->halfdim[1] = fabsf(aabb->halfdim[1]);
|
|
|
|
|
aabb->halfdim[2] = fabsf(aabb->halfdim[2]);
|
|
|
|
|
|
|
|
|
|
oedata->need_update = false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-22 10:52:39 -02:00
|
|
|
void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata)
|
2017-04-10 12:06:17 +02:00
|
|
|
{
|
2017-05-30 22:29:20 +02:00
|
|
|
EEVEE_LampsInfo *linfo = sldata->lamps;
|
2018-04-30 16:02:24 +02:00
|
|
|
GPUTextureFormat shadow_pool_format = GPU_R32F;
|
2017-03-17 00:00:46 +01:00
|
|
|
|
2018-01-21 17:25:10 +01:00
|
|
|
sldata->common_data.la_num_light = linfo->num_light;
|
|
|
|
|
|
2017-06-01 18:20:44 +02:00
|
|
|
/* Setup enough layers. */
|
|
|
|
|
/* Free textures if number mismatch. */
|
2017-09-05 21:02:17 +02:00
|
|
|
if (linfo->num_layer != linfo->cache_num_layer) {
|
2017-09-01 15:59:01 +02:00
|
|
|
DRW_TEXTURE_FREE_SAFE(sldata->shadow_pool);
|
2017-09-05 21:02:17 +02:00
|
|
|
linfo->cache_num_layer = linfo->num_layer;
|
2017-09-01 15:59:01 +02:00
|
|
|
linfo->update_flag |= LIGHT_UPDATE_SHADOW_CUBE;
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-01 18:39:39 +02:00
|
|
|
switch (linfo->shadow_method) {
|
2018-04-30 16:02:24 +02:00
|
|
|
case SHADOW_ESM: shadow_pool_format = ((linfo->shadow_high_bitdepth) ? GPU_R32F : GPU_R16F); break;
|
|
|
|
|
case SHADOW_VSM: shadow_pool_format = ((linfo->shadow_high_bitdepth) ? GPU_RG32F : GPU_RG16F); break;
|
2017-09-01 18:39:39 +02:00
|
|
|
default:
|
|
|
|
|
BLI_assert(!"Incorrect Shadow Method");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-09-01 15:59:01 +02:00
|
|
|
|
|
|
|
|
if (!sldata->shadow_cube_target) {
|
|
|
|
|
/* TODO render everything on the same 2d render target using clip planes and no Geom Shader. */
|
|
|
|
|
/* Cubemaps */
|
2017-11-20 14:28:24 +11:00
|
|
|
sldata->shadow_cube_target = DRW_texture_create_cube(
|
2018-04-30 16:02:24 +02:00
|
|
|
linfo->shadow_cube_target_size, GPU_DEPTH_COMPONENT24, 0, NULL);
|
2017-11-20 14:28:24 +11:00
|
|
|
sldata->shadow_cube_blur = DRW_texture_create_cube(
|
|
|
|
|
linfo->shadow_cube_target_size, shadow_pool_format, DRW_TEX_FILTER, NULL);
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-09-01 15:59:01 +02:00
|
|
|
|
|
|
|
|
if (!sldata->shadow_cascade_target) {
|
|
|
|
|
/* CSM */
|
|
|
|
|
sldata->shadow_cascade_target = DRW_texture_create_2D_array(
|
2018-04-30 16:02:24 +02:00
|
|
|
linfo->shadow_size, linfo->shadow_size, MAX_CASCADE_NUM, GPU_DEPTH_COMPONENT24, 0, NULL);
|
2017-09-08 20:21:57 +02:00
|
|
|
sldata->shadow_cascade_blur = DRW_texture_create_2D_array(
|
|
|
|
|
linfo->shadow_size, linfo->shadow_size, MAX_CASCADE_NUM, shadow_pool_format, DRW_TEX_FILTER, NULL);
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-09-01 15:59:01 +02:00
|
|
|
|
|
|
|
|
if (!sldata->shadow_pool) {
|
|
|
|
|
/* All shadows fit in this array */
|
|
|
|
|
sldata->shadow_pool = DRW_texture_create_2D_array(
|
2017-09-05 21:02:17 +02:00
|
|
|
linfo->shadow_size, linfo->shadow_size, max_ff(1, linfo->num_layer),
|
2017-09-01 15:59:01 +02:00
|
|
|
shadow_pool_format, DRW_TEX_FILTER, NULL);
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-01 15:59:01 +02:00
|
|
|
/* Render FB */
|
2018-03-25 17:46:48 +02:00
|
|
|
GPU_framebuffer_ensure_config(&sldata->shadow_cube_target_fb, {
|
|
|
|
|
GPU_ATTACHMENT_TEXTURE(sldata->shadow_cube_target)
|
|
|
|
|
});
|
|
|
|
|
GPU_framebuffer_ensure_config(&sldata->shadow_cascade_target_fb, {
|
|
|
|
|
GPU_ATTACHMENT_TEXTURE(sldata->shadow_cascade_target)
|
|
|
|
|
});
|
2017-04-10 12:06:17 +02:00
|
|
|
|
2017-09-01 15:59:01 +02:00
|
|
|
/* Storage FB */
|
2018-03-25 17:46:48 +02:00
|
|
|
GPU_framebuffer_ensure_config(&sldata->shadow_store_fb, {
|
|
|
|
|
GPU_ATTACHMENT_NONE,
|
|
|
|
|
GPU_ATTACHMENT_TEXTURE(sldata->shadow_pool)
|
|
|
|
|
});
|
2017-06-01 18:20:44 +02:00
|
|
|
|
|
|
|
|
/* Update Lamps UBOs. */
|
|
|
|
|
EEVEE_lights_update(sldata);
|
2017-03-17 00:00:46 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Update buffer with lamp data */
|
2018-01-09 18:01:56 +01:00
|
|
|
static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
|
2017-03-17 00:00:46 +01:00
|
|
|
{
|
2017-04-10 12:06:17 +02:00
|
|
|
Lamp *la = (Lamp *)ob->data;
|
|
|
|
|
float mat[4][4], scale[3], power;
|
2017-03-17 00:00:46 +01:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Position */
|
|
|
|
|
copy_v3_v3(evli->position, ob->obmat[3]);
|
2017-03-28 00:09:45 +02:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Color */
|
2017-05-29 12:37:34 +02:00
|
|
|
copy_v3_v3(evli->color, &la->r);
|
2017-03-28 00:09:45 +02:00
|
|
|
|
2018-05-02 18:39:17 +02:00
|
|
|
evli->spec = la->spec_fac;
|
|
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Influence Radius */
|
|
|
|
|
evli->dist = la->dist;
|
2017-03-28 00:09:45 +02:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Vectors */
|
|
|
|
|
normalize_m4_m4_ex(mat, ob->obmat, scale);
|
|
|
|
|
copy_v3_v3(evli->forwardvec, mat[2]);
|
|
|
|
|
normalize_v3(evli->forwardvec);
|
|
|
|
|
negate_v3(evli->forwardvec);
|
2017-03-28 00:09:45 +02:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
copy_v3_v3(evli->rightvec, mat[0]);
|
|
|
|
|
normalize_v3(evli->rightvec);
|
2017-03-28 00:09:45 +02:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
copy_v3_v3(evli->upvec, mat[1]);
|
|
|
|
|
normalize_v3(evli->upvec);
|
2017-03-28 00:09:45 +02:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Spot size & blend */
|
|
|
|
|
if (la->type == LA_SPOT) {
|
|
|
|
|
evli->sizex = scale[0] / scale[2];
|
|
|
|
|
evli->sizey = scale[1] / scale[2];
|
|
|
|
|
evli->spotsize = cosf(la->spotsize * 0.5f);
|
|
|
|
|
evli->spotblend = (1.0f - evli->spotsize) * la->spotblend;
|
2017-04-27 22:27:41 +02:00
|
|
|
evli->radius = max_ff(0.001f, la->area_size);
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
else if (la->type == LA_AREA) {
|
2017-04-27 22:27:41 +02:00
|
|
|
evli->sizex = max_ff(0.0001f, la->area_size * scale[0] * 0.5f);
|
2017-04-10 12:06:17 +02:00
|
|
|
if (la->area_shape == LA_AREA_RECT) {
|
2017-04-27 22:27:41 +02:00
|
|
|
evli->sizey = max_ff(0.0001f, la->area_sizey * scale[1] * 0.5f);
|
2017-03-29 23:45:07 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2017-04-27 22:27:41 +02:00
|
|
|
evli->sizey = max_ff(0.0001f, la->area_size * scale[1] * 0.5f);
|
2017-03-29 23:45:07 +02:00
|
|
|
}
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2017-04-27 22:27:41 +02:00
|
|
|
evli->radius = max_ff(0.001f, la->area_size);
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-03-28 00:09:45 +02:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Make illumination power constant */
|
|
|
|
|
if (la->type == LA_AREA) {
|
2017-10-07 15:57:14 +11:00
|
|
|
power = 1.0f / (evli->sizex * evli->sizey * 4.0f * M_PI) * /* 1/(w*h*Pi) */
|
|
|
|
|
80.0f; /* XXX : Empirical, Fit cycles power */
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
else if (la->type == LA_SPOT || la->type == LA_LOCAL) {
|
2017-10-07 15:57:14 +11:00
|
|
|
power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI) * /* 1/(4*r²*Pi²) */
|
2018-01-18 21:51:23 +01:00
|
|
|
M_PI * M_PI * 10.0; /* XXX : Empirical, Fit cycles power */
|
2017-03-31 01:07:30 +02:00
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* for point lights (a.k.a radius == 0.0) */
|
|
|
|
|
// power = M_PI * M_PI * 0.78; /* XXX : Empirical, Fit cycles power */
|
|
|
|
|
}
|
|
|
|
|
else {
|
2018-01-18 21:51:23 +01:00
|
|
|
power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI) * /* 1/(r²*Pi) */
|
|
|
|
|
12.5f; /* XXX : Empirical, Fit cycles power */
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-05-11 14:32:03 +02:00
|
|
|
mul_v3_fl(evli->color, power * la->energy);
|
2017-04-10 12:06:17 +02:00
|
|
|
|
|
|
|
|
/* Lamp Type */
|
|
|
|
|
evli->lamptype = (float)la->type;
|
|
|
|
|
|
|
|
|
|
/* No shadow by default */
|
|
|
|
|
evli->shadowid = -1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void eevee_shadow_cube_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_LampEngineData *led)
|
|
|
|
|
{
|
2018-01-11 14:08:21 +01:00
|
|
|
EEVEE_ShadowCubeData *sh_data = &led->data.scd;
|
2017-09-05 21:02:17 +02:00
|
|
|
EEVEE_Light *evli = linfo->light_data + sh_data->light_id;
|
|
|
|
|
EEVEE_Shadow *ubo_data = linfo->shadow_data + sh_data->shadow_id;
|
|
|
|
|
EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + sh_data->cube_id;
|
2017-04-10 12:06:17 +02:00
|
|
|
Lamp *la = (Lamp *)ob->data;
|
|
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
int sh_nbr = 1; /* TODO: MSM */
|
2017-04-10 12:06:17 +02:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
for (int i = 0; i < sh_nbr; ++i) {
|
|
|
|
|
/* TODO : choose MSM sample point here. */
|
|
|
|
|
copy_v3_v3(cube_data->position, ob->obmat[3]);
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
ubo_data->bias = 0.05f * la->bias;
|
|
|
|
|
ubo_data->near = la->clipsta;
|
|
|
|
|
ubo_data->far = la->clipend;
|
|
|
|
|
ubo_data->exp = (linfo->shadow_method == SHADOW_VSM) ? la->bleedbias : la->bleedexp;
|
2017-04-10 12:06:17 +02:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
evli->shadowid = (float)(sh_data->shadow_id);
|
|
|
|
|
ubo_data->shadow_start = (float)(sh_data->layer_id);
|
|
|
|
|
ubo_data->data_start = (float)(sh_data->cube_id);
|
|
|
|
|
ubo_data->multi_shadow_count = (float)(sh_nbr);
|
2017-11-22 04:51:21 +01:00
|
|
|
ubo_data->shadow_blur = la->soft * 0.02f; /* Used by translucence shadowmap blur */
|
2017-10-06 23:43:36 +02:00
|
|
|
|
|
|
|
|
ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
|
|
|
|
|
ubo_data->contact_bias = 0.05f * la->contact_bias;
|
|
|
|
|
ubo_data->contact_spread = la->contact_spread;
|
|
|
|
|
ubo_data->contact_thickness = la->contact_thickness;
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-20 13:07:24 +02:00
|
|
|
#define LERP(t, a, b) ((a) + (t) * ((b) - (a)))
|
|
|
|
|
|
2018-02-24 04:25:25 +01:00
|
|
|
static double round_to_digits(double value, int digits)
|
2017-04-21 16:43:14 +02:00
|
|
|
{
|
2018-02-24 04:25:25 +01:00
|
|
|
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. */
|
2017-04-21 16:43:14 +02:00
|
|
|
float minvec[3], maxvec[3];
|
|
|
|
|
|
|
|
|
|
/* compute the bounding box */
|
|
|
|
|
INIT_MINMAX(minvec, maxvec);
|
2017-10-07 15:57:14 +11:00
|
|
|
for (int i = 0; i < 8; ++i) {
|
2017-04-21 16:43:14 +02:00
|
|
|
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
|
2018-02-24 04:25:25 +01:00
|
|
|
/* 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);
|
2017-04-21 16:43:14 +02:00
|
|
|
|
2018-02-24 04:25:25 +01:00
|
|
|
/* 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);
|
2017-04-21 16:43:14 +02:00
|
|
|
if (rad > *r_radius) {
|
|
|
|
|
*r_radius = rad;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-24 04:25:25 +01:00
|
|
|
|
|
|
|
|
/* 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);
|
2017-04-21 16:43:14 +02:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-20 13:07:24 +02:00
|
|
|
static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_LampEngineData *led)
|
|
|
|
|
{
|
2017-09-07 15:31:11 +02:00
|
|
|
Lamp *la = (Lamp *)ob->data;
|
|
|
|
|
|
2017-04-20 13:07:24 +02:00
|
|
|
/* Camera Matrices */
|
|
|
|
|
float persmat[4][4], persinv[4][4];
|
2017-04-21 16:43:14 +02:00
|
|
|
float viewprojmat[4][4], projinv[4][4];
|
2017-09-07 15:31:11 +02:00
|
|
|
float view_near, view_far;
|
2017-04-21 16:43:14 +02:00
|
|
|
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);
|
2017-09-07 15:31:11 +02:00
|
|
|
view_near = near_v[2];
|
|
|
|
|
view_far = far_v[2]; /* TODO: Should be a shadow parameter */
|
2017-04-21 16:43:14 +02:00
|
|
|
if (is_persp) {
|
2017-09-07 15:31:11 +02:00
|
|
|
view_near /= near_v[3];
|
|
|
|
|
view_far /= far_v[3];
|
2017-04-21 16:43:14 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-20 13:07:24 +02:00
|
|
|
/* Lamps Matrices */
|
2017-09-05 21:02:17 +02:00
|
|
|
int sh_nbr = 1; /* TODO : MSM */
|
2017-09-07 15:31:11 +02:00
|
|
|
int cascade_nbr = la->cascade_count;
|
2017-04-20 13:07:24 +02:00
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
EEVEE_ShadowCascadeData *sh_data = &led->data.scad;
|
2017-09-05 21:02:17 +02:00
|
|
|
EEVEE_Light *evli = linfo->light_data + sh_data->light_id;
|
|
|
|
|
EEVEE_Shadow *ubo_data = linfo->shadow_data + sh_data->shadow_id;
|
|
|
|
|
EEVEE_ShadowCascade *cascade_data = linfo->shadow_cascade_data + sh_data->cascade_id;
|
2017-04-20 13:07:24 +02:00
|
|
|
|
2018-03-02 18:35:25 +01:00
|
|
|
/* obmat = Object Space > World Space */
|
|
|
|
|
/* viewmat = World Space > View Space */
|
2018-04-15 22:22:50 +02:00
|
|
|
float (*viewmat)[4] = sh_data->viewmat;
|
|
|
|
|
invert_m4_m4(viewmat, ob->obmat);
|
2018-03-02 18:35:25 +01:00
|
|
|
normalize_m4(viewmat);
|
2018-04-15 22:22:50 +02:00
|
|
|
invert_m4_m4(sh_data->viewinv, viewmat);
|
2018-03-02 18:35:25 +01:00
|
|
|
|
2017-04-20 13:07:24 +02:00
|
|
|
/* The technique consists into splitting
|
|
|
|
|
* the view frustum into several sub-frustum
|
|
|
|
|
* that are individually receiving one shadow map */
|
|
|
|
|
|
2017-09-07 15:31:11 +02:00
|
|
|
float csm_start, csm_end;
|
|
|
|
|
|
|
|
|
|
if (is_persp) {
|
|
|
|
|
csm_start = view_near;
|
|
|
|
|
csm_end = max_ff(view_far, -la->cascade_max_dist);
|
|
|
|
|
/* Avoid artifacts */
|
|
|
|
|
csm_end = min_ff(view_near, csm_end);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
csm_start = -view_far;
|
|
|
|
|
csm_end = view_far;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-21 16:43:14 +02:00
|
|
|
/* init near/far */
|
|
|
|
|
for (int c = 0; c < MAX_CASCADE_NUM; ++c) {
|
2017-09-07 15:31:11 +02:00
|
|
|
cascade_data->split_start[c] = csm_end;
|
|
|
|
|
cascade_data->split_end[c] = csm_end;
|
2017-04-21 16:43:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compute split planes */
|
2017-09-07 15:31:11 +02:00
|
|
|
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(viewprojmat, 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(viewprojmat, p);
|
|
|
|
|
splits_end_ndc[cascade_nbr - 1] = p[2];
|
|
|
|
|
if (is_persp) {
|
|
|
|
|
splits_end_ndc[cascade_nbr - 1] /= p[3];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cascade_data->split_start[0] = csm_start;
|
|
|
|
|
cascade_data->split_end[cascade_nbr - 1] = csm_end;
|
2017-04-21 16:43:14 +02:00
|
|
|
|
2017-09-07 15:31:11 +02:00
|
|
|
for (int c = 1; c < cascade_nbr; ++c) {
|
2017-04-21 16:43:14 +02:00
|
|
|
/* View Space */
|
2017-09-07 15:31:11 +02:00
|
|
|
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);
|
2017-04-21 16:43:14 +02:00
|
|
|
|
|
|
|
|
if (is_persp) {
|
2017-09-07 15:31:11 +02:00
|
|
|
cascade_data->split_start[c] = LERP(la->cascade_exponent, linear_split, exp_split);
|
2017-04-21 16:43:14 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2017-09-07 15:31:11 +02:00
|
|
|
cascade_data->split_start[c] = linear_split;
|
2017-04-21 16:43:14 +02:00
|
|
|
}
|
2017-10-07 15:57:14 +11:00
|
|
|
cascade_data->split_end[c - 1] = cascade_data->split_start[c];
|
2017-09-07 15:31:11 +02:00
|
|
|
|
|
|
|
|
/* Add some overlap for smooth transition */
|
2017-10-07 15:57:14 +11:00
|
|
|
cascade_data->split_start[c] = LERP(la->cascade_fade, cascade_data->split_end[c - 1],
|
|
|
|
|
(c > 1) ? cascade_data->split_end[c - 2] : cascade_data->split_start[0]);
|
2017-04-21 16:43:14 +02:00
|
|
|
|
|
|
|
|
/* NDC Space */
|
2017-09-07 15:31:11 +02:00
|
|
|
{
|
|
|
|
|
float p[4] = {1.0f, 1.0f, cascade_data->split_start[c], 1.0f};
|
|
|
|
|
/* TODO: we don't need full m4 multiply here */
|
|
|
|
|
mul_m4_v4(viewprojmat, p);
|
|
|
|
|
splits_start_ndc[c] = p[2];
|
2017-04-21 16:43:14 +02:00
|
|
|
|
2017-09-07 15:31:11 +02:00
|
|
|
if (is_persp) {
|
|
|
|
|
splits_start_ndc[c] /= p[3];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2017-10-07 15:57:14 +11:00
|
|
|
float p[4] = {1.0f, 1.0f, cascade_data->split_end[c - 1], 1.0f};
|
2017-09-07 15:31:11 +02:00
|
|
|
/* TODO: we don't need full m4 multiply here */
|
|
|
|
|
mul_m4_v4(viewprojmat, p);
|
2017-10-07 15:57:14 +11:00
|
|
|
splits_end_ndc[c - 1] = p[2];
|
2017-09-07 15:31:11 +02:00
|
|
|
|
|
|
|
|
if (is_persp) {
|
2017-10-07 15:57:14 +11:00
|
|
|
splits_end_ndc[c - 1] /= p[3];
|
2017-09-07 15:31:11 +02:00
|
|
|
}
|
2017-04-21 16:43:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-07 15:31:11 +02:00
|
|
|
/* Set last cascade split fade distance into the first split_start. */
|
2017-10-07 15:57:14 +11:00
|
|
|
float prev_split = (cascade_nbr > 1) ? cascade_data->split_end[cascade_nbr - 2] : cascade_data->split_start[0];
|
|
|
|
|
cascade_data->split_start[0] = LERP(la->cascade_fade, cascade_data->split_end[cascade_nbr - 1], prev_split);
|
2017-09-07 15:31:11 +02:00
|
|
|
|
2017-04-20 13:07:24 +02:00
|
|
|
/* For each cascade */
|
2017-09-05 21:02:17 +02:00
|
|
|
for (int c = 0; c < cascade_nbr; ++c) {
|
2018-04-15 22:22:50 +02:00
|
|
|
float (*projmat)[4] = sh_data->projmat[c];
|
2017-09-05 21:02:17 +02:00
|
|
|
/* Given 8 frustum corners */
|
2018-02-24 04:25:25 +01:00
|
|
|
float corners[8][3] = {
|
2017-04-20 13:07:24 +02:00
|
|
|
/* Near Cap */
|
2018-02-24 04:25:25 +01:00
|
|
|
{-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]},
|
2017-04-21 16:43:14 +02:00
|
|
|
/* Far Cap */
|
2018-02-24 04:25:25 +01:00
|
|
|
{-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]}
|
2017-04-20 13:07:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Transform them into world space */
|
2017-10-07 15:57:14 +11:00
|
|
|
for (int i = 0; i < 8; ++i) {
|
2018-02-24 04:25:25 +01:00
|
|
|
mul_project_m4_v3(persinv, corners[i]);
|
2017-04-20 13:07:24 +02:00
|
|
|
}
|
|
|
|
|
|
2018-02-24 04:25:25 +01:00
|
|
|
float center[3];
|
|
|
|
|
frustum_min_bounding_sphere(corners, center, &(sh_data->radius[c]));
|
2017-09-07 15:31:11 +02:00
|
|
|
|
2018-02-24 04:25:25 +01:00
|
|
|
/* Project into lightspace */
|
|
|
|
|
mul_mat3_m4_v3(viewmat, center);
|
2017-04-21 16:43:14 +02:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
/* Snap projection center to nearest texel to cancel shimmering. */
|
2017-04-21 16:43:14 +02:00
|
|
|
float shadow_origin[2], shadow_texco[2];
|
2017-11-20 14:28:24 +11:00
|
|
|
/* Light to texture space. */
|
|
|
|
|
mul_v2_v2fl(shadow_origin, center, linfo->shadow_size / (2.0f * sh_data->radius[c]));
|
2017-04-21 16:43:14 +02:00
|
|
|
|
|
|
|
|
/* Find the nearest texel. */
|
2018-02-24 04:25:25 +01:00
|
|
|
shadow_texco[0] = roundf(shadow_origin[0]);
|
|
|
|
|
shadow_texco[1] = roundf(shadow_origin[1]);
|
2017-04-21 16:43:14 +02:00
|
|
|
|
|
|
|
|
/* Compute offset. */
|
|
|
|
|
sub_v2_v2(shadow_texco, shadow_origin);
|
2017-09-05 21:02:17 +02:00
|
|
|
mul_v2_fl(shadow_texco, (2.0f * sh_data->radius[c]) / linfo->shadow_size); /* Texture to light space. */
|
2017-04-20 13:07:24 +02:00
|
|
|
|
2017-04-21 16:43:14 +02:00
|
|
|
/* Apply offset. */
|
|
|
|
|
add_v2_v2(center, shadow_texco);
|
|
|
|
|
|
|
|
|
|
/* Expand the projection to cover frustum range */
|
2018-04-15 22:22:50 +02:00
|
|
|
rctf rect_cascade;
|
2018-03-02 18:35:25 +01:00
|
|
|
BLI_rctf_init_pt_radius(&rect_cascade, center, sh_data->radius[c]);
|
2017-04-21 16:43:14 +02:00
|
|
|
orthographic_m4(projmat,
|
2018-03-02 18:35:25 +01:00
|
|
|
rect_cascade.xmin, rect_cascade.xmax,
|
|
|
|
|
rect_cascade.ymin, rect_cascade.ymax,
|
2017-04-21 16:43:14 +02:00
|
|
|
la->clipsta, la->clipend);
|
2017-04-20 13:07:24 +02:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
mul_m4_m4m4(sh_data->viewprojmat[c], projmat, viewmat);
|
|
|
|
|
mul_m4_m4m4(cascade_data->shadowmat[c], texcomat, sh_data->viewprojmat[c]);
|
2017-04-21 16:43:14 +02:00
|
|
|
}
|
2017-04-20 13:07:24 +02:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
ubo_data->bias = 0.05f * la->bias;
|
|
|
|
|
ubo_data->near = la->clipsta;
|
|
|
|
|
ubo_data->far = la->clipend;
|
|
|
|
|
ubo_data->exp = (linfo->shadow_method == SHADOW_VSM) ? la->bleedbias : la->bleedexp;
|
|
|
|
|
|
|
|
|
|
evli->shadowid = (float)(sh_data->shadow_id);
|
|
|
|
|
ubo_data->shadow_start = (float)(sh_data->layer_id);
|
|
|
|
|
ubo_data->data_start = (float)(sh_data->cascade_id);
|
|
|
|
|
ubo_data->multi_shadow_count = (float)(sh_nbr);
|
2017-11-22 04:51:21 +01:00
|
|
|
ubo_data->shadow_blur = la->soft * 0.02f; /* Used by translucence shadowmap blur */
|
2017-10-06 23:43:36 +02:00
|
|
|
|
|
|
|
|
ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
|
|
|
|
|
ubo_data->contact_bias = 0.05f * la->contact_bias;
|
|
|
|
|
ubo_data->contact_spread = la->contact_spread;
|
|
|
|
|
ubo_data->contact_thickness = la->contact_thickness;
|
2017-04-20 13:07:24 +02:00
|
|
|
}
|
|
|
|
|
|
2017-06-01 18:20:44 +02:00
|
|
|
/* Used for checking if object is inside the shadow volume. */
|
2018-01-11 14:08:21 +01:00
|
|
|
static bool sphere_bbox_intersect(const EEVEE_BoundSphere *bs, const EEVEE_BoundBox *bb)
|
2017-06-01 18:20:44 +02:00
|
|
|
{
|
2018-01-11 14:08:21 +01:00
|
|
|
/* We are testing using a rougher AABB vs AABB test instead of full AABB vs Sphere. */
|
|
|
|
|
/* TODO test speed with AABB vs Sphere. */
|
|
|
|
|
bool x = fabsf(bb->center[0] - bs->center[0]) <= (bb->halfdim[0] + bs->radius);
|
|
|
|
|
bool y = fabsf(bb->center[1] - bs->center[1]) <= (bb->halfdim[1] + bs->radius);
|
|
|
|
|
bool z = fabsf(bb->center[2] - bs->center[2]) <= (bb->halfdim[2] + bs->radius);
|
2017-06-01 18:20:44 +02:00
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
return x && y && z;
|
2017-06-01 18:20:44 +02:00
|
|
|
}
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
void EEVEE_lights_update(EEVEE_ViewLayerData *sldata)
|
2017-06-01 18:20:44 +02:00
|
|
|
{
|
2018-01-11 14:08:21 +01:00
|
|
|
EEVEE_LampsInfo *linfo = sldata->lamps;
|
|
|
|
|
Object *ob;
|
|
|
|
|
int i;
|
|
|
|
|
char *flag;
|
|
|
|
|
EEVEE_ShadowCaster *shcaster;
|
|
|
|
|
EEVEE_BoundSphere *bsphere;
|
|
|
|
|
EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer;
|
|
|
|
|
EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer;
|
|
|
|
|
|
|
|
|
|
EEVEE_LightBits update_bits = {{0}};
|
|
|
|
|
if ((linfo->update_flag & LIGHT_UPDATE_SHADOW_CUBE) != 0) {
|
|
|
|
|
/* Update all lights. */
|
|
|
|
|
lightbits_set_all(&update_bits, true);
|
2017-06-01 18:20:44 +02:00
|
|
|
}
|
2018-01-11 14:08:21 +01:00
|
|
|
else {
|
|
|
|
|
/* Search for deleted shadow casters and if shcaster WAS in shadow radius. */
|
|
|
|
|
/* No need to run this if we already update all lamps. */
|
|
|
|
|
EEVEE_LightBits past_bits = {{0}};
|
|
|
|
|
EEVEE_LightBits curr_bits = {{0}};
|
|
|
|
|
shcaster = backbuffer->shadow_casters;
|
|
|
|
|
flag = backbuffer->flags;
|
|
|
|
|
for (i = 0; i < backbuffer->count; ++i, ++flag, ++shcaster) {
|
|
|
|
|
/* If the shadowcaster has been deleted or updated. */
|
|
|
|
|
if (*flag != 0) {
|
|
|
|
|
/* Add the lamps that were intersecting with its BBox. */
|
|
|
|
|
lightbits_or(&past_bits, &shcaster->bits);
|
|
|
|
|
}
|
2017-06-01 18:20:44 +02:00
|
|
|
}
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Convert old bits to new bits and add result to final update bits. */
|
|
|
|
|
/* NOTE: This might be overkill since all lights are tagged to refresh if
|
|
|
|
|
* the light count changes. */
|
|
|
|
|
lightbits_convert(&curr_bits, &past_bits, linfo->new_shadow_id, MAX_LIGHT);
|
|
|
|
|
lightbits_or(&update_bits, &curr_bits);
|
2017-06-01 18:20:44 +02:00
|
|
|
}
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Search for updates in current shadow casters. */
|
|
|
|
|
shcaster = frontbuffer->shadow_casters;
|
|
|
|
|
flag = frontbuffer->flags;
|
|
|
|
|
for (i = 0; i < frontbuffer->count; i++, flag++, shcaster++) {
|
|
|
|
|
/* Run intersection checks to fill the bitfields. */
|
|
|
|
|
bsphere = linfo->shadow_bounds;
|
|
|
|
|
for (int j = 0; j < linfo->cpu_cube_ct; j++, bsphere++) {
|
|
|
|
|
bool iter = sphere_bbox_intersect(bsphere, &shcaster->bbox);
|
|
|
|
|
lightbits_set_single(&shcaster->bits, j, iter);
|
2017-06-01 18:20:44 +02:00
|
|
|
}
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Only add to final bits if objects has been updated. */
|
|
|
|
|
if (*flag != 0) {
|
|
|
|
|
lightbits_or(&update_bits, &shcaster->bits);
|
2017-06-01 18:20:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Setup shadow cube in UBO and tag for update if necessary. */
|
|
|
|
|
for (i = 0; (i < MAX_SHADOW_CUBE) && (ob = linfo->shadow_cube_ref[i]); i++) {
|
|
|
|
|
EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
|
2017-06-01 18:20:44 +02:00
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
eevee_shadow_cube_setup(ob, linfo, led);
|
|
|
|
|
if (lightbits_get(&update_bits, i) != 0) {
|
2017-06-01 18:20:44 +02:00
|
|
|
led->need_update = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
/* Resize shcasters buffers if too big. */
|
|
|
|
|
if (frontbuffer->alloc_count - frontbuffer->count > SHADOW_CASTER_ALLOC_CHUNK) {
|
|
|
|
|
frontbuffer->alloc_count = (frontbuffer->count / SHADOW_CASTER_ALLOC_CHUNK) * SHADOW_CASTER_ALLOC_CHUNK;
|
|
|
|
|
frontbuffer->alloc_count += (frontbuffer->count % SHADOW_CASTER_ALLOC_CHUNK != 0) ? SHADOW_CASTER_ALLOC_CHUNK : 0;
|
|
|
|
|
frontbuffer->shadow_casters = MEM_reallocN(frontbuffer->shadow_casters, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
|
|
|
|
|
frontbuffer->flags = MEM_reallocN(frontbuffer->flags, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* this refresh lamps shadow buffers */
|
2017-11-22 10:52:39 -02:00
|
|
|
void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
|
2017-04-10 12:06:17 +02:00
|
|
|
{
|
2017-05-30 22:29:20 +02:00
|
|
|
EEVEE_LampsInfo *linfo = sldata->lamps;
|
2017-04-10 12:06:17 +02:00
|
|
|
Object *ob;
|
|
|
|
|
int i;
|
|
|
|
|
|
2018-03-02 18:35:25 +01:00
|
|
|
DRWMatrixState saved_mats;
|
|
|
|
|
|
|
|
|
|
/* We need to save the Matrices before overidding them */
|
|
|
|
|
DRW_viewport_matrix_get_all(&saved_mats);
|
|
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Cube Shadow Maps */
|
2017-09-10 03:07:55 +02:00
|
|
|
DRW_stats_group_start("Cube Shadow Maps");
|
2017-04-10 12:06:17 +02:00
|
|
|
/* Render each shadow to one layer of the array */
|
|
|
|
|
for (i = 0; (ob = linfo->shadow_cube_ref[i]) && (i < MAX_SHADOW_CUBE); i++) {
|
2017-11-29 10:36:58 +01:00
|
|
|
EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
|
2017-06-02 20:50:04 +02:00
|
|
|
Lamp *la = (Lamp *)ob->data;
|
2017-06-01 18:20:44 +02:00
|
|
|
|
2017-11-29 11:59:15 +01:00
|
|
|
if (!led->need_update) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-06-01 18:20:44 +02:00
|
|
|
|
2018-04-15 22:22:50 +02:00
|
|
|
DRWMatrixState render_mats;
|
|
|
|
|
float (*winmat)[4] = render_mats.mat[DRW_MAT_WIN];
|
|
|
|
|
float (*viewmat)[4] = render_mats.mat[DRW_MAT_VIEW];
|
|
|
|
|
float (*persmat)[4] = render_mats.mat[DRW_MAT_PERS];
|
|
|
|
|
|
2017-11-29 11:59:15 +01:00
|
|
|
EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
|
2018-01-11 14:08:21 +01:00
|
|
|
EEVEE_ShadowCubeData *evscd = &led->data.scd;
|
2017-07-11 01:10:57 +02:00
|
|
|
|
2018-04-15 22:22:50 +02:00
|
|
|
perspective_m4(winmat, -la->clipsta, la->clipsta, -la->clipsta, la->clipsta, la->clipsta, la->clipend);
|
2018-03-02 18:35:25 +01:00
|
|
|
|
2017-11-29 11:59:15 +01:00
|
|
|
srd->clip_near = la->clipsta;
|
|
|
|
|
srd->clip_far = la->clipend;
|
|
|
|
|
copy_v3_v3(srd->position, ob->obmat[3]);
|
2017-09-08 20:21:57 +02:00
|
|
|
|
2017-11-29 11:59:15 +01:00
|
|
|
DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
|
2017-09-09 21:11:22 +02:00
|
|
|
|
2017-11-29 11:59:15 +01:00
|
|
|
/* Render shadow cube */
|
2018-04-15 22:22:50 +02:00
|
|
|
/* Render 6 faces separatly: seems to be faster for the general case.
|
|
|
|
|
* The only time it's more beneficial is when the CPU culling overhead
|
|
|
|
|
* outweight the instancing overhead. which is rarelly the case. */
|
|
|
|
|
for (int j = 0; j < 6; j++) {
|
|
|
|
|
/* TODO optimize */
|
|
|
|
|
float tmp[4][4];
|
|
|
|
|
unit_m4(tmp);
|
|
|
|
|
negate_v3_v3(tmp[3], srd->position);
|
|
|
|
|
mul_m4_m4m4(viewmat, cubefacemat[j], tmp);
|
|
|
|
|
mul_m4_m4m4(persmat, winmat, viewmat);
|
|
|
|
|
invert_m4_m4(render_mats.mat[DRW_MAT_WININV], winmat);
|
|
|
|
|
invert_m4_m4(render_mats.mat[DRW_MAT_VIEWINV], viewmat);
|
|
|
|
|
invert_m4_m4(render_mats.mat[DRW_MAT_PERSINV], persmat);
|
|
|
|
|
|
|
|
|
|
DRW_viewport_matrix_override_set_all(&render_mats);
|
|
|
|
|
|
|
|
|
|
GPU_framebuffer_texture_cubeface_attach(sldata->shadow_cube_target_fb,
|
|
|
|
|
sldata->shadow_cube_target, 0, j, 0);
|
|
|
|
|
GPU_framebuffer_bind(sldata->shadow_cube_target_fb);
|
|
|
|
|
GPU_framebuffer_clear_depth(sldata->shadow_cube_target_fb, 1.0f);
|
|
|
|
|
DRW_draw_pass(psl->shadow_pass);
|
|
|
|
|
}
|
2017-09-09 21:11:22 +02:00
|
|
|
|
2017-11-29 11:59:15 +01:00
|
|
|
/* 0.001f is arbitrary, but it should be relatively small so that filter size is not too big. */
|
|
|
|
|
float filter_texture_size = la->soft * 0.001f;
|
|
|
|
|
float filter_pixel_size = ceil(filter_texture_size / linfo->shadow_render_data.cube_texel_size);
|
|
|
|
|
linfo->filter_size = linfo->shadow_render_data.cube_texel_size * ((filter_pixel_size > 1.0f) ? 1.5f : 0.0f);
|
|
|
|
|
|
|
|
|
|
/* TODO: OPTI: Filter all faces in one/two draw call */
|
|
|
|
|
for (linfo->current_shadow_face = 0;
|
|
|
|
|
linfo->current_shadow_face < 6;
|
|
|
|
|
linfo->current_shadow_face++)
|
|
|
|
|
{
|
|
|
|
|
/* Copy using a small 3x3 box filter */
|
2018-03-25 17:46:48 +02:00
|
|
|
GPU_framebuffer_texture_cubeface_attach(sldata->shadow_store_fb, sldata->shadow_cube_blur, 0,
|
|
|
|
|
linfo->current_shadow_face, 0);
|
|
|
|
|
GPU_framebuffer_bind(sldata->shadow_store_fb);
|
2017-11-29 11:59:15 +01:00
|
|
|
DRW_draw_pass(psl->shadow_cube_copy_pass);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Push it to shadowmap array */
|
|
|
|
|
|
|
|
|
|
/* Adjust constants if concentric samples change. */
|
|
|
|
|
const float max_filter_size = 7.5f;
|
|
|
|
|
const float previous_box_filter_size = 9.0f; /* Dunno why but that works. */
|
|
|
|
|
const int max_sample = 256;
|
2017-05-20 13:16:14 +02:00
|
|
|
|
2017-11-29 11:59:15 +01:00
|
|
|
if (filter_pixel_size > 2.0f) {
|
|
|
|
|
linfo->filter_size = linfo->shadow_render_data.cube_texel_size * max_filter_size * previous_box_filter_size;
|
|
|
|
|
filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
|
|
|
|
|
/* Compute number of concentric samples. Depends directly on filter size. */
|
|
|
|
|
float pix_size_sqr = filter_pixel_size * filter_pixel_size;
|
|
|
|
|
srd->shadow_samples_ct = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
|
2017-06-01 18:20:44 +02:00
|
|
|
}
|
2017-11-29 11:59:15 +01:00
|
|
|
else {
|
|
|
|
|
linfo->filter_size = 0.0f;
|
|
|
|
|
srd->shadow_samples_ct = 4;
|
|
|
|
|
}
|
|
|
|
|
srd->shadow_inv_samples_ct = 1.0f / (float)srd->shadow_samples_ct;
|
|
|
|
|
DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
|
|
|
|
|
|
2018-03-25 17:46:48 +02:00
|
|
|
GPU_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, evscd->layer_id, 0);
|
|
|
|
|
GPU_framebuffer_bind(sldata->shadow_store_fb);
|
2017-11-29 11:59:15 +01:00
|
|
|
DRW_draw_pass(psl->shadow_cube_store_pass);
|
|
|
|
|
|
|
|
|
|
led->need_update = false;
|
2017-04-10 12:06:17 +02:00
|
|
|
}
|
2017-06-01 18:20:44 +02:00
|
|
|
linfo->update_flag &= ~LIGHT_UPDATE_SHADOW_CUBE;
|
2017-09-10 03:07:55 +02:00
|
|
|
DRW_stats_group_end();
|
2017-03-31 01:07:30 +02:00
|
|
|
|
2018-03-06 02:51:51 +01:00
|
|
|
DRW_viewport_matrix_override_set_all(&saved_mats);
|
|
|
|
|
|
2017-04-20 13:07:24 +02:00
|
|
|
/* Cascaded Shadow Maps */
|
2017-09-10 03:07:55 +02:00
|
|
|
DRW_stats_group_start("Cascaded Shadow Maps");
|
2017-09-05 21:02:17 +02:00
|
|
|
for (i = 0; (ob = linfo->shadow_cascade_ref[i]) && (i < MAX_SHADOW_CASCADE); i++) {
|
2017-11-29 10:36:58 +01:00
|
|
|
EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
|
2017-09-05 21:02:17 +02:00
|
|
|
Lamp *la = (Lamp *)ob->data;
|
|
|
|
|
|
2018-01-11 14:08:21 +01:00
|
|
|
EEVEE_ShadowCascadeData *evscd = &led->data.scad;
|
2017-09-05 21:02:17 +02:00
|
|
|
EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
|
|
|
|
|
|
2018-04-15 22:22:50 +02:00
|
|
|
DRWMatrixState render_mats;
|
|
|
|
|
float (*winmat)[4] = render_mats.mat[DRW_MAT_WIN];
|
|
|
|
|
float (*viewmat)[4] = render_mats.mat[DRW_MAT_VIEW];
|
|
|
|
|
float (*persmat)[4] = render_mats.mat[DRW_MAT_PERS];
|
|
|
|
|
|
2017-09-10 03:07:55 +02:00
|
|
|
eevee_shadow_cascade_setup(ob, linfo, led);
|
|
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
srd->clip_near = la->clipsta;
|
|
|
|
|
srd->clip_far = la->clipend;
|
2018-04-15 22:22:50 +02:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
|
|
|
|
|
|
2018-04-15 22:22:50 +02:00
|
|
|
copy_m4_m4(viewmat, evscd->viewmat);
|
|
|
|
|
invert_m4_m4(render_mats.mat[DRW_MAT_VIEWINV], viewmat);
|
2018-03-02 18:35:25 +01:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
/* Render shadow cascades */
|
2018-04-15 22:22:50 +02:00
|
|
|
/* Render cascade separatly: seems to be faster for the general case.
|
|
|
|
|
* The only time it's more beneficial is when the CPU culling overhead
|
|
|
|
|
* outweight the instancing overhead. which is rarelly the case. */
|
|
|
|
|
for (int j = 0; j < la->cascade_count; j++) {
|
|
|
|
|
copy_m4_m4(winmat, evscd->projmat[j]);
|
|
|
|
|
copy_m4_m4(persmat, evscd->viewprojmat[j]);
|
|
|
|
|
invert_m4_m4(render_mats.mat[DRW_MAT_WININV], winmat);
|
|
|
|
|
invert_m4_m4(render_mats.mat[DRW_MAT_PERSINV], persmat);
|
|
|
|
|
|
|
|
|
|
DRW_viewport_matrix_override_set_all(&render_mats);
|
|
|
|
|
|
|
|
|
|
GPU_framebuffer_texture_layer_attach(sldata->shadow_cascade_target_fb,
|
|
|
|
|
sldata->shadow_cascade_target, 0, j, 0);
|
|
|
|
|
GPU_framebuffer_bind(sldata->shadow_cascade_target_fb);
|
|
|
|
|
GPU_framebuffer_clear_depth(sldata->shadow_cascade_target_fb, 1.0f);
|
|
|
|
|
DRW_draw_pass(psl->shadow_pass);
|
|
|
|
|
}
|
2017-09-05 21:02:17 +02:00
|
|
|
|
2017-09-09 21:11:22 +02:00
|
|
|
/* TODO: OPTI: Filter all cascade in one/two draw call */
|
2017-09-05 21:02:17 +02:00
|
|
|
for (linfo->current_shadow_cascade = 0;
|
2017-09-07 15:31:11 +02:00
|
|
|
linfo->current_shadow_cascade < la->cascade_count;
|
2017-09-05 21:02:17 +02:00
|
|
|
++linfo->current_shadow_cascade)
|
|
|
|
|
{
|
2017-09-09 21:11:22 +02:00
|
|
|
/* 0.01f factor to convert to percentage */
|
|
|
|
|
float filter_texture_size = la->soft * 0.01f / evscd->radius[linfo->current_shadow_cascade];
|
|
|
|
|
float filter_pixel_size = ceil(linfo->shadow_size * filter_texture_size);
|
|
|
|
|
|
2017-09-08 20:21:57 +02:00
|
|
|
/* Copy using a small 3x3 box filter */
|
2017-09-09 21:11:22 +02:00
|
|
|
linfo->filter_size = linfo->shadow_render_data.stored_texel_size * ((filter_pixel_size > 1.0f) ? 1.0f : 0.0f);
|
2018-03-25 17:46:48 +02:00
|
|
|
GPU_framebuffer_texture_layer_attach(
|
2017-11-20 14:28:24 +11:00
|
|
|
sldata->shadow_store_fb, sldata->shadow_cascade_blur, 0, linfo->current_shadow_cascade, 0);
|
2018-03-25 17:46:48 +02:00
|
|
|
GPU_framebuffer_bind(sldata->shadow_store_fb);
|
2017-09-08 20:21:57 +02:00
|
|
|
DRW_draw_pass(psl->shadow_cascade_copy_pass);
|
2017-09-05 21:02:17 +02:00
|
|
|
|
2017-09-08 20:21:57 +02:00
|
|
|
/* Push it to shadowmap array and blur more */
|
2017-09-09 21:11:22 +02:00
|
|
|
|
|
|
|
|
/* Adjust constants if concentric samples change. */
|
|
|
|
|
const float max_filter_size = 7.5f;
|
|
|
|
|
const float previous_box_filter_size = 3.2f; /* Arbitrary: less banding */
|
|
|
|
|
const int max_sample = 256;
|
|
|
|
|
|
|
|
|
|
if (filter_pixel_size > 2.0f) {
|
|
|
|
|
linfo->filter_size = linfo->shadow_render_data.stored_texel_size * max_filter_size * previous_box_filter_size;
|
|
|
|
|
filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
|
|
|
|
|
/* Compute number of concentric samples. Depends directly on filter size. */
|
|
|
|
|
float pix_size_sqr = filter_pixel_size * filter_pixel_size;
|
|
|
|
|
srd->shadow_samples_ct = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
linfo->filter_size = 0.0f;
|
|
|
|
|
srd->shadow_samples_ct = 4;
|
|
|
|
|
}
|
|
|
|
|
srd->shadow_inv_samples_ct = 1.0f / (float)srd->shadow_samples_ct;
|
|
|
|
|
DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
|
|
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
int layer = evscd->layer_id + linfo->current_shadow_cascade;
|
2018-03-25 17:46:48 +02:00
|
|
|
GPU_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, layer, 0);
|
|
|
|
|
GPU_framebuffer_bind(sldata->shadow_store_fb);
|
2017-09-05 21:02:17 +02:00
|
|
|
DRW_draw_pass(psl->shadow_cascade_store_pass);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-10 03:07:55 +02:00
|
|
|
DRW_stats_group_end();
|
|
|
|
|
|
2018-03-02 18:35:25 +01:00
|
|
|
DRW_viewport_matrix_override_set_all(&saved_mats);
|
|
|
|
|
|
2017-09-10 03:07:55 +02:00
|
|
|
DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data);
|
|
|
|
|
DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */
|
2017-03-17 00:00:46 +01:00
|
|
|
}
|
2017-05-20 13:16:14 +02:00
|
|
|
|
|
|
|
|
void EEVEE_lights_free(void)
|
|
|
|
|
{
|
|
|
|
|
DRW_SHADER_FREE_SAFE(e_data.shadow_sh);
|
2017-09-01 18:39:39 +02:00
|
|
|
for (int i = 0; i < SHADOW_METHOD_MAX; ++i) {
|
|
|
|
|
DRW_SHADER_FREE_SAFE(e_data.shadow_store_cube_sh[i]);
|
|
|
|
|
DRW_SHADER_FREE_SAFE(e_data.shadow_store_cascade_sh[i]);
|
2017-09-08 20:21:57 +02:00
|
|
|
DRW_SHADER_FREE_SAFE(e_data.shadow_copy_cube_sh[i]);
|
|
|
|
|
DRW_SHADER_FREE_SAFE(e_data.shadow_copy_cascade_sh[i]);
|
2017-09-01 18:39:39 +02:00
|
|
|
}
|
2017-11-20 14:11:45 +11:00
|
|
|
}
|