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_lightprobes.c
Campbell Barton 0b2d1badec Cleanup: use post increment/decrement
When the result isn't used, prefer post increment/decrement
(already used nearly everywhere in Blender).
2019-09-08 00:23:25 +10:00

1320 lines
50 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 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "DRW_render.h"
#include "BLI_utildefines.h"
#include "BLI_rand.h"
#include "DNA_world_types.h"
#include "DNA_texture_types.h"
#include "DNA_image_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_view3d_types.h"
#include "BKE_collection.h"
#include "BKE_object.h"
#include "MEM_guardedalloc.h"
#include "GPU_material.h"
#include "GPU_texture.h"
#include "DEG_depsgraph_query.h"
#include "eevee_lightcache.h"
#include "eevee_private.h"
#include "WM_api.h"
#include "WM_types.h"
static struct {
struct GPUTexture *hammersley;
struct GPUTexture *planar_pool_placeholder;
struct GPUTexture *depth_placeholder;
struct GPUTexture *depth_array_placeholder;
struct GPUTexture *cube_face_minmaxz;
struct GPUVertFormat *format_probe_display_cube;
struct GPUVertFormat *format_probe_display_planar;
} e_data = {NULL}; /* Engine data */
/* *********** FUNCTIONS *********** */
/* TODO find a better way than this. This does not support dupli objects if
* the original object is hidden. */
bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data)
{
EEVEE_ObjectEngineData *oed = (EEVEE_ObjectEngineData *)user_data;
/* test disabled if group is NULL */
if (oed == NULL || oed->test_data->collection == NULL) {
return vis_in;
}
if (oed->test_data->cached == false) {
oed->ob_vis_dirty = true;
}
/* early out, don't need to compute ob_vis yet. */
if (vis_in == false) {
return vis_in;
}
if (oed->ob_vis_dirty) {
oed->ob_vis_dirty = false;
oed->ob_vis = BKE_collection_has_object_recursive(oed->test_data->collection, oed->ob);
oed->ob_vis = (oed->test_data->invert) ? !oed->ob_vis : oed->ob_vis;
}
return vis_in && oed->ob_vis;
}
static struct GPUTexture *create_hammersley_sample_texture(int samples)
{
struct GPUTexture *tex;
float(*texels)[2] = MEM_mallocN(sizeof(float[2]) * samples, "hammersley_tex");
int i;
for (i = 0; i < samples; i++) {
double dphi;
BLI_hammersley_1d(i, &dphi);
float phi = (float)dphi * 2.0f * M_PI;
texels[i][0] = cosf(phi);
texels[i][1] = sinf(phi);
}
tex = DRW_texture_create_1d(samples, GPU_RG16F, DRW_TEX_WRAP, (float *)texels);
MEM_freeN(texels);
return tex;
}
static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref)
{
EEVEE_TextureList *txl = vedata->txl;
/* XXX TODO OPTIMISATION : This is a complete waist of texture memory.
* Instead of allocating each planar probe for each viewport,
* only alloc them once using the biggest viewport resolution. */
const float *viewport_size = DRW_viewport_size_get();
/* TODO get screen percentage from layer setting */
// const DRWContextState *draw_ctx = DRW_context_state_get();
// ViewLayer *view_layer = draw_ctx->view_layer;
float screen_percentage = 1.0f;
int width = max_ii(1, (int)(viewport_size[0] * screen_percentage));
int height = max_ii(1, (int)(viewport_size[1] * screen_percentage));
/* Fix case were the pool was allocated width the dummy size (1,1,1). */
if (txl->planar_pool && (num_planar_ref > 0) &&
(GPU_texture_width(txl->planar_pool) != width ||
GPU_texture_height(txl->planar_pool) != height)) {
DRW_TEXTURE_FREE_SAFE(txl->planar_pool);
DRW_TEXTURE_FREE_SAFE(txl->planar_depth);
}
/* We need an Array texture so allocate it ourself */
if (!txl->planar_pool) {
if (num_planar_ref > 0) {
txl->planar_pool = DRW_texture_create_2d_array(width,
height,
max_ii(1, num_planar_ref),
GPU_R11F_G11F_B10F,
DRW_TEX_FILTER | DRW_TEX_MIPMAP,
NULL);
txl->planar_depth = DRW_texture_create_2d_array(
width, height, max_ii(1, num_planar_ref), GPU_DEPTH_COMPONENT24, 0, NULL);
}
else if (num_planar_ref == 0) {
/* Makes Opengl Happy : Create a placeholder texture that will never be sampled but still
* bound to shader. */
txl->planar_pool = DRW_texture_create_2d_array(
1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
txl->planar_depth = DRW_texture_create_2d_array(1, 1, 1, GPU_DEPTH_COMPONENT24, 0, NULL);
}
}
}
void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
EEVEE_StorageList *stl = vedata->stl;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if (!e_data.hammersley) {
EEVEE_shaders_lightprobe_shaders_init();
e_data.hammersley = create_hammersley_sample_texture(HAMMERSLEY_SIZE);
}
memset(stl->g_data->bake_views, 0, sizeof(stl->g_data->bake_views));
memset(stl->g_data->cube_views, 0, sizeof(stl->g_data->cube_views));
memset(stl->g_data->world_views, 0, sizeof(stl->g_data->world_views));
memset(stl->g_data->planar_views, 0, sizeof(stl->g_data->planar_views));
/* Use fallback if we don't have gpu texture allocated an we cannot restore them. */
bool use_fallback_lightcache = (scene_eval->eevee.light_cache == NULL) ||
((scene_eval->eevee.light_cache->grid_tx.tex == NULL) &&
(scene_eval->eevee.light_cache->grid_tx.data == NULL)) ||
((scene_eval->eevee.light_cache->cube_tx.tex == NULL) &&
(scene_eval->eevee.light_cache->cube_tx.data == NULL));
if (use_fallback_lightcache && (sldata->fallback_lightcache == NULL)) {
#if defined(IRRADIANCE_SH_L2)
int grid_res = 4;
#elif defined(IRRADIANCE_CUBEMAP)
int grid_res = 8;
#elif defined(IRRADIANCE_HL2)
int grid_res = 4;
#endif
int cube_res = OCTAHEDRAL_SIZE_FROM_CUBESIZE(scene_eval->eevee.gi_cubemap_resolution);
int vis_res = scene_eval->eevee.gi_visibility_resolution;
sldata->fallback_lightcache = EEVEE_lightcache_create(
1, 1, cube_res, vis_res, (int[3]){grid_res, grid_res, 1});
}
stl->g_data->light_cache = (use_fallback_lightcache) ? sldata->fallback_lightcache :
scene_eval->eevee.light_cache;
EEVEE_lightcache_load(stl->g_data->light_cache);
if (!sldata->probes) {
sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo");
sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL);
sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL);
sldata->planar_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR,
NULL);
}
common_data->prb_num_planar = 0;
common_data->prb_num_render_cube = 1;
common_data->prb_num_render_grid = 1;
common_data->spec_toggle = true;
common_data->ssr_toggle = true;
common_data->ssrefract_toggle = true;
common_data->sss_toggle = true;
/* Placeholder planar pool: used when rendering planar reflections (avoid dependency loop). */
if (!e_data.planar_pool_placeholder) {
e_data.planar_pool_placeholder = DRW_texture_create_2d_array(
1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER, NULL);
}
}
/* Only init the passes useful for rendering the light cache. */
void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
GPUTexture *rt_color,
GPUTexture *rt_depth)
{
EEVEE_PassList *psl = vedata->psl;
LightCache *light_cache = vedata->stl->g_data->light_cache;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
{
DRW_PASS_CREATE(psl->probe_glossy_compute, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_glossy_sh_get(),
psl->probe_glossy_compute);
DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1);
DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->samples_len_inv, 1);
DRW_shgroup_uniform_float(grp, "roughnessSquared", &pinfo->roughness, 1);
DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1);
DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1);
DRW_shgroup_uniform_float(grp, "texelSize", &pinfo->texel_size, 1);
DRW_shgroup_uniform_float(grp, "paddingSize", &pinfo->padding_size, 1);
DRW_shgroup_uniform_float(grp, "fireflyFactor", &pinfo->firefly_fac, 1);
DRW_shgroup_uniform_int(grp, "Layer", &pinfo->layer, 1);
DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
// DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter);
DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call(grp, geom, NULL);
}
{
DRW_PASS_CREATE(psl->probe_diffuse_compute, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_diffuse_sh_get(),
psl->probe_diffuse_compute);
#ifdef IRRADIANCE_SH_L2
DRW_shgroup_uniform_int(grp, "probeSize", &pinfo->shres, 1);
#else
DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->samples_len_inv, 1);
DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1);
DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1);
DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
#endif
DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1);
DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call(grp, geom, NULL);
}
{
DRW_PASS_CREATE(psl->probe_visibility_compute, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_visibility_sh_get(),
psl->probe_visibility_compute);
DRW_shgroup_uniform_int(grp, "outputSize", &pinfo->shres, 1);
DRW_shgroup_uniform_float(grp, "visibilityRange", &pinfo->visibility_range, 1);
DRW_shgroup_uniform_float(grp, "visibilityBlur", &pinfo->visibility_blur, 1);
DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->samples_len_inv, 1);
DRW_shgroup_uniform_float(grp, "storedTexelSize", &pinfo->texel_size, 1);
DRW_shgroup_uniform_float(grp, "nearClip", &pinfo->near_clip, 1);
DRW_shgroup_uniform_float(grp, "farClip", &pinfo->far_clip, 1);
DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
DRW_shgroup_uniform_texture(grp, "probeDepth", rt_depth);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call(grp, geom, NULL);
}
{
DRW_PASS_CREATE(psl->probe_grid_fill, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_grid_fill_sh_get(),
psl->probe_grid_fill);
DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &light_cache->grid_tx.tex);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call(grp, geom, NULL);
}
}
void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_TextureList *txl = vedata->txl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
LightCache *lcache = stl->g_data->light_cache;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
pinfo->num_planar = 0;
pinfo->vis_data.collection = NULL;
pinfo->do_grid_update = false;
pinfo->do_cube_update = false;
{
DRW_PASS_CREATE(psl->probe_background, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRWShadingGroup *grp = NULL;
Scene *scene = draw_ctx->scene;
World *wo = scene->world;
const float *col = G_draw.block.colorBackground;
/* LookDev */
EEVEE_lookdev_cache_init(vedata, &grp, psl->probe_background, 1.0f, wo, pinfo);
/* END */
if (!grp && wo) {
col = &wo->horr;
if (wo->use_nodes && wo->nodetree) {
static float error_col[3] = {1.0f, 0.0f, 1.0f};
static float queue_col[3] = {0.5f, 0.5f, 0.5f};
struct GPUMaterial *gpumat = EEVEE_material_world_lightprobe_get(scene, wo);
eGPUMaterialStatus status = GPU_material_status(gpumat);
switch (status) {
case GPU_MAT_SUCCESS:
grp = DRW_shgroup_material_create(gpumat, psl->probe_background);
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
/* TODO (fclem): remove those (need to clean the GLSL files). */
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_call(grp, geom, NULL);
break;
case GPU_MAT_QUEUED:
stl->g_data->queued_shaders_count++;
col = queue_col;
break;
default:
col = error_col;
break;
}
}
}
/* Fallback if shader fails or if not using nodetree. */
if (grp == NULL) {
grp = DRW_shgroup_create(EEVEE_shaders_probe_default_sh_get(), psl->probe_background);
DRW_shgroup_uniform_vec3(grp, "color", col, 1);
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
DRW_shgroup_call(grp, geom, NULL);
}
}
if (DRW_state_draw_support() && !LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) {
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
DRW_STATE_CULL_BACK;
DRW_PASS_CREATE(psl->probe_display, state);
/* Cube Display */
if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS && lcache->cube_len > 1) {
int cube_len = lcache->cube_len - 1; /* don't count the world. */
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_cube_display_sh_get(),
psl->probe_display);
DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_vec3(grp, "screen_vecs[0]", DRW_viewport_screenvecs_get(), 2);
DRW_shgroup_uniform_float_copy(
grp, "sphere_size", scene_eval->eevee.gi_cubemap_draw_size * 0.5f);
/* TODO (fclem) get rid of those UBO. */
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
DRW_shgroup_call_procedural_triangles(grp, NULL, cube_len * 2);
}
/* Grid Display */
if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) {
EEVEE_LightGrid *egrid = lcache->grid_data + 1;
for (int p = 1; p < lcache->grid_len; p++, egrid++) {
DRWShadingGroup *shgrp = DRW_shgroup_create(EEVEE_shaders_probe_grid_display_sh_get(),
psl->probe_display);
DRW_shgroup_uniform_int(shgrp, "offset", &egrid->offset, 1);
DRW_shgroup_uniform_ivec3(shgrp, "grid_resolution", egrid->resolution, 1);
DRW_shgroup_uniform_vec3(shgrp, "corner", egrid->corner, 1);
DRW_shgroup_uniform_vec3(shgrp, "increment_x", egrid->increment_x, 1);
DRW_shgroup_uniform_vec3(shgrp, "increment_y", egrid->increment_y, 1);
DRW_shgroup_uniform_vec3(shgrp, "increment_z", egrid->increment_z, 1);
DRW_shgroup_uniform_vec3(shgrp, "screen_vecs[0]", DRW_viewport_screenvecs_get(), 2);
DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex);
DRW_shgroup_uniform_float_copy(
shgrp, "sphere_size", scene_eval->eevee.gi_irradiance_draw_size * 0.5f);
/* TODO (fclem) get rid of those UBO. */
DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo);
DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo);
int tri_count = egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2] * 2;
DRW_shgroup_call_procedural_triangles(shgrp, NULL, tri_count);
}
}
/* Planar Display */
DRW_shgroup_instance_format(e_data.format_probe_display_planar,
{
{"probe_id", DRW_ATTR_INT, 1},
{"probe_mat", DRW_ATTR_FLOAT, 16},
});
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_display_sh_get(),
psl->probe_display);
DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &txl->planar_pool);
stl->g_data->planar_display_shgrp = DRW_shgroup_call_buffer_instance(
grp, e_data.format_probe_display_planar, DRW_cache_quad_get());
}
else {
stl->g_data->planar_display_shgrp = NULL;
}
}
static bool eevee_lightprobes_culling_test(Object *ob)
{
LightProbe *probe = (LightProbe *)ob->data;
switch (probe->type) {
case LIGHTPROBE_TYPE_PLANAR: {
/* See if this planar probe is inside the view frustum. If not, no need to update it. */
/* NOTE: this could be bypassed if we want feedback loop mirrors for rendering. */
BoundBox bbox;
float tmp[4][4];
const float min[3] = {-1.0f, -1.0f, -1.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
BKE_boundbox_init_from_minmax(&bbox, min, max);
copy_m4_m4(tmp, ob->obmat);
normalize_v3(tmp[2]);
mul_v3_fl(tmp[2], probe->distinf);
for (int v = 0; v < 8; v++) {
mul_m4_v3(tmp, bbox.vec[v]);
}
const DRWView *default_view = DRW_view_default_get();
return DRW_culling_box_test(default_view, &bbox);
}
case LIGHTPROBE_TYPE_CUBE:
return true; /* TODO */
case LIGHTPROBE_TYPE_GRID:
return true; /* TODO */
}
BLI_assert(0);
return true;
}
void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob)
{
EEVEE_LightProbesInfo *pinfo = sldata->probes;
LightProbe *probe = (LightProbe *)ob->data;
if ((probe->type == LIGHTPROBE_TYPE_CUBE && pinfo->num_cube >= MAX_PROBE) ||
(probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= MAX_PROBE) ||
(probe->type == LIGHTPROBE_TYPE_PLANAR && pinfo->num_planar >= MAX_PLANAR)) {
printf("Too many probes in the view !!!\n");
return;
}
if (probe->type == LIGHTPROBE_TYPE_PLANAR) {
/* TODO(fclem): Culling should be done after cache generation.
* This is needed for future draw cache persistence. */
if (!eevee_lightprobes_culling_test(ob)) {
return; /* Culled */
}
EEVEE_lightprobes_planar_data_from_object(
ob, &pinfo->planar_data[pinfo->num_planar], &pinfo->planar_vis_tests[pinfo->num_planar]);
/* Debug Display */
DRWCallBuffer *grp = vedata->stl->g_data->planar_display_shgrp;
if (grp && (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) {
DRW_buffer_add_entry(grp, &pinfo->num_planar, ob->obmat);
}
pinfo->num_planar++;
}
else {
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob);
if (ped->need_update) {
if (probe->type == LIGHTPROBE_TYPE_GRID) {
pinfo->do_grid_update = true;
}
else {
pinfo->do_cube_update = true;
}
ped->need_update = false;
}
}
}
void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset)
{
LightProbe *probe = (LightProbe *)ob->data;
copy_v3_v3_int(egrid->resolution, &probe->grid_resolution_x);
/* Save current offset and advance it for the next grid. */
egrid->offset = *offset;
*offset += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2];
/* Add one for level 0 */
float fac = 1.0f / max_ff(1e-8f, probe->falloff);
egrid->attenuation_scale = fac / max_ff(1e-8f, probe->distinf);
egrid->attenuation_bias = fac;
/* Update transforms */
float cell_dim[3], half_cell_dim[3];
cell_dim[0] = 2.0f / egrid->resolution[0];
cell_dim[1] = 2.0f / egrid->resolution[1];
cell_dim[2] = 2.0f / egrid->resolution[2];
mul_v3_v3fl(half_cell_dim, cell_dim, 0.5f);
/* Matrix converting world space to cell ranges. */
invert_m4_m4(egrid->mat, ob->obmat);
/* First cell. */
copy_v3_fl(egrid->corner, -1.0f);
add_v3_v3(egrid->corner, half_cell_dim);
mul_m4_v3(ob->obmat, egrid->corner);
/* Opposite neighbor cell. */
copy_v3_fl3(egrid->increment_x, cell_dim[0], 0.0f, 0.0f);
add_v3_v3(egrid->increment_x, half_cell_dim);
add_v3_fl(egrid->increment_x, -1.0f);
mul_m4_v3(ob->obmat, egrid->increment_x);
sub_v3_v3(egrid->increment_x, egrid->corner);
copy_v3_fl3(egrid->increment_y, 0.0f, cell_dim[1], 0.0f);
add_v3_v3(egrid->increment_y, half_cell_dim);
add_v3_fl(egrid->increment_y, -1.0f);
mul_m4_v3(ob->obmat, egrid->increment_y);
sub_v3_v3(egrid->increment_y, egrid->corner);
copy_v3_fl3(egrid->increment_z, 0.0f, 0.0f, cell_dim[2]);
add_v3_v3(egrid->increment_z, half_cell_dim);
add_v3_fl(egrid->increment_z, -1.0f);
mul_m4_v3(ob->obmat, egrid->increment_z);
sub_v3_v3(egrid->increment_z, egrid->corner);
/* Visibility bias */
egrid->visibility_bias = 0.05f * probe->vis_bias;
egrid->visibility_bleed = probe->vis_bleedbias;
egrid->visibility_range = 1.0f + sqrtf(max_fff(len_squared_v3(egrid->increment_x),
len_squared_v3(egrid->increment_y),
len_squared_v3(egrid->increment_z)));
}
void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe)
{
LightProbe *probe = (LightProbe *)ob->data;
/* Update transforms */
copy_v3_v3(eprobe->position, ob->obmat[3]);
/* Attenuation */
eprobe->attenuation_type = probe->attenuation_type;
eprobe->attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff);
unit_m4(eprobe->attenuationmat);
scale_m4_fl(eprobe->attenuationmat, probe->distinf);
mul_m4_m4m4(eprobe->attenuationmat, ob->obmat, eprobe->attenuationmat);
invert_m4(eprobe->attenuationmat);
/* Parallax */
unit_m4(eprobe->parallaxmat);
if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) {
eprobe->parallax_type = probe->parallax_type;
scale_m4_fl(eprobe->parallaxmat, probe->distpar);
}
else {
eprobe->parallax_type = probe->attenuation_type;
scale_m4_fl(eprobe->parallaxmat, probe->distinf);
}
mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat);
invert_m4(eprobe->parallaxmat);
}
void EEVEE_lightprobes_planar_data_from_object(Object *ob,
EEVEE_PlanarReflection *eplanar,
EEVEE_LightProbeVisTest *vis_test)
{
LightProbe *probe = (LightProbe *)ob->data;
float normat[4][4], imat[4][4];
vis_test->collection = probe->visibility_grp;
vis_test->invert = probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP;
vis_test->cached = false;
/* Computing mtx : matrix that mirror position around object's XY plane. */
normalize_m4_m4(normat, ob->obmat); /* object > world */
invert_m4_m4(imat, normat); /* world > object */
/* XY reflection plane */
imat[0][2] = -imat[0][2];
imat[1][2] = -imat[1][2];
imat[2][2] = -imat[2][2];
imat[3][2] = -imat[3][2]; /* world > object > mirrored obj */
mul_m4_m4m4(eplanar->mtx, normat, imat); /* world > object > mirrored obj > world */
/* Compute clip plane equation / normal. */
copy_v3_v3(eplanar->plane_equation, ob->obmat[2]);
normalize_v3(eplanar->plane_equation); /* plane normal */
eplanar->plane_equation[3] = -dot_v3v3(eplanar->plane_equation, ob->obmat[3]);
eplanar->clipsta = probe->clipsta;
/* Compute XY clip planes. */
normalize_v3_v3(eplanar->clip_vec_x, ob->obmat[0]);
normalize_v3_v3(eplanar->clip_vec_y, ob->obmat[1]);
float vec[3] = {0.0f, 0.0f, 0.0f};
vec[0] = 1.0f;
vec[1] = 0.0f;
vec[2] = 0.0f;
mul_m4_v3(ob->obmat, vec); /* Point on the edge */
eplanar->clip_edge_x_pos = dot_v3v3(eplanar->clip_vec_x, vec);
vec[0] = 0.0f;
vec[1] = 1.0f;
vec[2] = 0.0f;
mul_m4_v3(ob->obmat, vec); /* Point on the edge */
eplanar->clip_edge_y_pos = dot_v3v3(eplanar->clip_vec_y, vec);
vec[0] = -1.0f;
vec[1] = 0.0f;
vec[2] = 0.0f;
mul_m4_v3(ob->obmat, vec); /* Point on the edge */
eplanar->clip_edge_x_neg = dot_v3v3(eplanar->clip_vec_x, vec);
vec[0] = 0.0f;
vec[1] = -1.0f;
vec[2] = 0.0f;
mul_m4_v3(ob->obmat, vec); /* Point on the edge */
eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec);
/* Facing factors */
float max_angle = max_ff(1e-2f, 1.0f - probe->falloff) * M_PI * 0.5f;
float min_angle = 0.0f;
eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle));
eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale;
/* Distance factors */
float max_dist = probe->distinf;
float min_dist = min_ff(1.0f - 1e-8f, 1.0f - probe->falloff) * probe->distinf;
eplanar->attenuation_scale = -1.0f / max_ff(1e-8f, max_dist - min_dist);
eplanar->attenuation_bias = max_dist * -eplanar->attenuation_scale;
}
static void lightbake_planar_ensure_view(EEVEE_PlanarReflection *eplanar,
const DRWView *main_view,
DRWView **r_planar_view)
{
float winmat[4][4], viewmat[4][4];
DRW_view_viewmat_get(main_view, viewmat, false);
/* Temporal sampling jitter should be already applied to the DRW_MAT_WIN. */
DRW_view_winmat_get(main_view, winmat, false);
/* Invert X to avoid flipping the triangle facing direction. */
winmat[0][0] = -winmat[0][0];
winmat[1][0] = -winmat[1][0];
winmat[2][0] = -winmat[2][0];
winmat[3][0] = -winmat[3][0];
/* Reflect Camera Matrix. */
mul_m4_m4m4(viewmat, viewmat, eplanar->mtx);
if (*r_planar_view == NULL) {
*r_planar_view = DRW_view_create(
viewmat, winmat, NULL, NULL, EEVEE_lightprobes_obj_visibility_cb);
/* Compute offset plane equation (fix missing texels near reflection plane). */
float clip_plane[4];
copy_v4_v4(clip_plane, eplanar->plane_equation);
clip_plane[3] += eplanar->clipsta;
/* Set clipping plane */
DRW_view_clip_planes_set(*r_planar_view, &clip_plane, 1);
}
else {
DRW_view_update(*r_planar_view, viewmat, winmat, NULL, NULL);
}
}
static void eevee_lightprobes_extract_from_cache(EEVEE_LightProbesInfo *pinfo, LightCache *lcache)
{
/* copy the entire cache for now (up to MAX_PROBE) */
/* TODO Frutum cull to only add visible probes. */
memcpy(pinfo->probe_data,
lcache->cube_data,
sizeof(EEVEE_LightProbe) * max_ii(1, min_ii(lcache->cube_len, MAX_PROBE)));
/* TODO compute the max number of grid based on sample count. */
memcpy(pinfo->grid_data,
lcache->grid_data,
sizeof(EEVEE_LightGrid) * max_ii(1, min_ii(lcache->grid_len, MAX_GRID)));
}
void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
LightCache *light_cache = stl->g_data->light_cache;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
eevee_lightprobes_extract_from_cache(sldata->probes, light_cache);
DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data);
DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data);
/* For shading, save max level of the octahedron map */
sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len - 1.0f;
sldata->common_data.prb_lod_planar_max = (float)MAX_PLANAR_LOD_LEVEL;
sldata->common_data.prb_irradiance_vis_size = light_cache->vis_res;
sldata->common_data.prb_irradiance_smooth = SQUARE(scene_eval->eevee.gi_irradiance_smoothing);
sldata->common_data.prb_num_render_cube = max_ii(1, light_cache->cube_len);
sldata->common_data.prb_num_render_grid = max_ii(1, light_cache->grid_len);
sldata->common_data.prb_num_planar = pinfo->num_planar;
if (pinfo->num_planar != pinfo->cache_num_planar) {
DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_pool);
DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_depth);
pinfo->cache_num_planar = pinfo->num_planar;
}
planar_pool_ensure_alloc(vedata, pinfo->num_planar);
/* If light-cache auto-update is enable we tag the relevant part
* of the cache to update and fire up a baking job. */
if (!DRW_state_is_image_render() && !DRW_state_is_opengl_render() &&
(pinfo->do_grid_update || pinfo->do_cube_update)) {
BLI_assert(draw_ctx->evil_C);
if (draw_ctx->scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) {
Scene *scene_orig = DEG_get_input_scene(draw_ctx->depsgraph);
if (scene_orig->eevee.light_cache != NULL) {
if (pinfo->do_grid_update) {
scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_GRID;
}
/* If we update grid we need to update the cubemaps too.
* So always refresh cubemaps. */
scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_CUBE;
/* Tag the lightcache to auto update. */
scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_AUTO;
/* Use a notifier to trigger the operator after drawing. */
WM_event_add_notifier(draw_ctx->evil_C, NC_LIGHTPROBE, scene_orig);
}
}
}
if (pinfo->num_planar > 0) {
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
DRW_PASS_CREATE(psl->probe_planar_downsample_ps, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_downsample_sh_get(),
psl->probe_planar_downsample_ps);
DRW_shgroup_uniform_texture_ref(grp, "source", &txl->planar_pool);
DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
DRW_shgroup_call_procedural_triangles(grp, NULL, pinfo->num_planar);
}
}
/* -------------------------------------------------------------------- */
/** \name Rendering
* \{ */
typedef struct EEVEE_BakeRenderData {
EEVEE_Data *vedata;
EEVEE_ViewLayerData *sldata;
struct GPUFrameBuffer **face_fb; /* should contain 6 framebuffer */
} EEVEE_BakeRenderData;
static void render_cubemap(void (*callback)(int face, EEVEE_BakeRenderData *user_data),
EEVEE_BakeRenderData *user_data,
const float pos[3],
float near,
float far,
bool do_culling)
{
EEVEE_StorageList *stl = user_data->vedata->stl;
DRWView **views = do_culling ? stl->g_data->bake_views : stl->g_data->world_views;
float winmat[4][4], viewmat[4][4];
perspective_m4(winmat, -near, near, -near, near, near, far);
/* Prepare views at the same time for faster culling. */
for (int i = 0; i < 6; i++) {
unit_m4(viewmat);
negate_v3_v3(viewmat[3], pos);
mul_m4_m4m4(viewmat, cubefacemat[i], viewmat);
if (do_culling) {
if (views[i] == NULL) {
views[i] = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
}
else {
DRW_view_update(views[i], viewmat, winmat, NULL, NULL);
}
}
else {
if (views[i] == NULL) {
const DRWView *default_view = DRW_view_default_get();
views[i] = DRW_view_create_sub(default_view, viewmat, winmat);
}
else {
DRW_view_update_sub(views[i], viewmat, winmat);
}
}
}
for (int i = 0; i < 6; i++) {
DRW_view_set_active(views[i]);
callback(i, user_data);
}
}
static void render_reflections(void (*callback)(int face, EEVEE_BakeRenderData *user_data),
EEVEE_BakeRenderData *user_data,
EEVEE_PlanarReflection *planar_data,
int ref_count)
{
EEVEE_StorageList *stl = user_data->vedata->stl;
DRWView *main_view = stl->effects->taa_view;
DRWView **views = stl->g_data->planar_views;
/* Prepare views at the same time for faster culling. */
for (int i = 0; i < ref_count; i++) {
lightbake_planar_ensure_view(&planar_data[i], main_view, &views[i]);
}
for (int i = 0; i < ref_count; i++) {
DRW_view_set_active(views[i]);
callback(i, user_data);
}
}
static void lightbake_render_world_face(int face, EEVEE_BakeRenderData *user_data)
{
EEVEE_PassList *psl = user_data->vedata->psl;
struct GPUFrameBuffer **face_fb = user_data->face_fb;
/* For world probe, we don't need to clear the color buffer
* since we render the background directly. */
GPU_framebuffer_bind(face_fb[face]);
GPU_framebuffer_clear_depth(face_fb[face], 1.0f);
DRW_draw_pass(psl->probe_background);
}
void EEVEE_lightbake_render_world(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
struct GPUFrameBuffer *face_fb[6])
{
EEVEE_BakeRenderData brdata = {
.vedata = vedata,
.face_fb = face_fb,
};
render_cubemap(lightbake_render_world_face, &brdata, (float[3]){0.0f}, 1.0f, 10.0f, false);
}
static void lightbake_render_scene_face(int face, EEVEE_BakeRenderData *user_data)
{
EEVEE_ViewLayerData *sldata = user_data->sldata;
EEVEE_PassList *psl = user_data->vedata->psl;
EEVEE_PrivateData *g_data = user_data->vedata->stl->g_data;
DRWView **views = g_data->bake_views;
struct GPUFrameBuffer **face_fb = user_data->face_fb;
/* Be sure that cascaded shadow maps are updated. */
EEVEE_shadows_draw(sldata, user_data->vedata, views[face]);
GPU_framebuffer_bind(face_fb[face]);
GPU_framebuffer_clear_depth(face_fb[face], 1.0f);
DRW_draw_pass(psl->depth_pass);
DRW_draw_pass(psl->depth_pass_cull);
DRW_draw_pass(psl->probe_background);
EEVEE_materials_draw_opaque(sldata, psl);
DRW_draw_pass(psl->sss_pass); /* Only output standard pass */
DRW_draw_pass(psl->sss_pass_cull);
DRW_draw_pass(psl->transparent_pass);
}
/* Render the scene to the probe_rt texture. */
void EEVEE_lightbake_render_scene(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
struct GPUFrameBuffer *face_fb[6],
const float pos[3],
float near_clip,
float far_clip)
{
EEVEE_BakeRenderData brdata = {
.vedata = vedata,
.sldata = sldata,
.face_fb = face_fb,
};
render_cubemap(lightbake_render_scene_face, &brdata, pos, near_clip, far_clip, true);
}
static void lightbake_render_scene_reflected(int layer, EEVEE_BakeRenderData *user_data)
{
EEVEE_Data *vedata = user_data->vedata;
EEVEE_ViewLayerData *sldata = user_data->sldata;
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
GPU_framebuffer_ensure_config(&fbl->planarref_fb,
{GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer),
GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer)});
/* Use visibility info for this planar reflection. */
pinfo->vis_data = pinfo->planar_vis_tests[layer];
/* Avoid using the texture attached to framebuffer when rendering. */
/* XXX */
GPUTexture *tmp_planar_pool = txl->planar_pool;
GPUTexture *tmp_planar_depth = txl->planar_depth;
txl->planar_pool = e_data.planar_pool_placeholder;
txl->planar_depth = e_data.depth_array_placeholder;
DRW_stats_group_start("Planar Reflection");
/* Be sure that cascaded shadow maps are updated. */
EEVEE_shadows_draw(sldata, vedata, stl->g_data->planar_views[layer]);
GPU_framebuffer_bind(fbl->planarref_fb);
GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0);
float prev_background_alpha = vedata->stl->g_data->background_alpha;
vedata->stl->g_data->background_alpha = 1.0f;
/* Slight modification: we handle refraction as normal
* shading and don't do SSRefraction. */
DRW_draw_pass(psl->depth_pass_clip);
DRW_draw_pass(psl->depth_pass_clip_cull);
DRW_draw_pass(psl->refract_depth_pass_clip);
DRW_draw_pass(psl->refract_depth_pass_clip_cull);
DRW_draw_pass(psl->probe_background);
EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer);
EEVEE_occlusion_compute(sldata, vedata, tmp_planar_depth, layer);
GPU_framebuffer_bind(fbl->planarref_fb);
/* Shading pass */
EEVEE_materials_draw_opaque(sldata, psl);
DRW_draw_pass(psl->sss_pass); /* Only output standard pass */
DRW_draw_pass(psl->sss_pass_cull);
DRW_draw_pass(psl->refract_pass);
/* Transparent */
if (DRW_state_is_image_render()) {
/* Do the reordering only for offline because it can be costly. */
DRW_pass_sort_shgroup_z(psl->transparent_pass);
}
DRW_draw_pass(psl->transparent_pass);
DRW_stats_group_end();
/* Restore */
txl->planar_pool = tmp_planar_pool;
txl->planar_depth = tmp_planar_depth;
vedata->stl->g_data->background_alpha = prev_background_alpha;
}
static void eevee_lightbake_render_scene_to_planars(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata)
{
EEVEE_BakeRenderData brdata = {
.vedata = vedata,
.sldata = sldata,
};
render_reflections(lightbake_render_scene_reflected,
&brdata,
sldata->probes->planar_data,
sldata->probes->num_planar);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Filtering
* \{ */
/* Glossy filter rt_color to light_cache->cube_tx.tex at index probe_idx */
void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
struct GPUTexture *rt_color,
struct GPUFrameBuffer *fb,
int probe_idx,
float intensity,
int maxlevel,
float filter_quality,
float firefly_fac)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
LightCache *light_cache = vedata->stl->g_data->light_cache;
float target_size = (float)GPU_texture_width(rt_color);
/* Max lod used from the render target probe */
pinfo->lod_rt_max = floorf(log2f(target_size)) - 2.0f;
pinfo->intensity_fac = intensity;
/* Start fresh */
GPU_framebuffer_ensure_config(&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE});
/* 2 - Let gpu create Mipmaps for Filtered Importance Sampling. */
/* Bind next framebuffer to be able to gen. mips for probe_rt. */
EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max));
/* 3 - Render to probe array to the specified layer, do prefiltering. */
int mipsize = GPU_texture_width(light_cache->cube_tx.tex);
for (int i = 0; i < maxlevel + 1; i++) {
float bias = 0.0f;
pinfo->texel_size = 1.0f / (float)mipsize;
pinfo->padding_size = (i == maxlevel) ? 0 : (float)(1 << (maxlevel - i - 1));
pinfo->padding_size *= pinfo->texel_size;
pinfo->layer = probe_idx;
pinfo->roughness = i / (float)maxlevel;
pinfo->roughness *= pinfo->roughness; /* Disney Roughness */
pinfo->roughness *= pinfo->roughness; /* Distribute Roughness accros lod more evenly */
CLAMP(pinfo->roughness, 1e-8f, 0.99999f); /* Avoid artifacts */
#if 1 /* Variable Sample count and bias (fast) */
switch (i) {
case 0:
pinfo->samples_len = 1.0f;
bias = -1.0f;
break;
case 1:
pinfo->samples_len = 32.0f;
bias = 1.0f;
break;
case 2:
pinfo->samples_len = 40.0f;
bias = 2.0f;
break;
case 3:
pinfo->samples_len = 64.0f;
bias = 2.0f;
break;
default:
pinfo->samples_len = 128.0f;
bias = 2.0f;
break;
}
#else /* Constant Sample count (slow) */
pinfo->samples_len = 1024.0f;
#endif
/* Cannot go higher than HAMMERSLEY_SIZE */
CLAMP(filter_quality, 1.0f, 8.0f);
pinfo->samples_len *= filter_quality;
pinfo->samples_len_inv = 1.0f / pinfo->samples_len;
pinfo->lodfactor = bias +
0.5f * log((float)(target_size * target_size) * pinfo->samples_len_inv) /
log(2);
pinfo->firefly_fac = (firefly_fac > 0.0) ? firefly_fac : 1e16;
GPU_framebuffer_ensure_config(
&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i)});
GPU_framebuffer_bind(fb);
GPU_framebuffer_viewport_set(fb, 0, 0, mipsize, mipsize);
DRW_draw_pass(psl->probe_glossy_compute);
mipsize /= 2;
CLAMP_MIN(mipsize, 1);
}
}
/* Diffuse filter rt_color to light_cache->grid_tx.tex at index grid_offset */
void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
struct GPUTexture *rt_color,
struct GPUFrameBuffer *fb,
int grid_offset,
float intensity)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
LightCache *light_cache = vedata->stl->g_data->light_cache;
float target_size = (float)GPU_texture_width(rt_color);
pinfo->intensity_fac = intensity;
/* find cell position on the virtual 3D texture */
/* NOTE : Keep in sync with load_irradiance_cell() */
#if defined(IRRADIANCE_SH_L2)
int size[2] = {3, 3};
#elif defined(IRRADIANCE_CUBEMAP)
int size[2] = {8, 8};
pinfo->samples_len = 1024.0f;
#elif defined(IRRADIANCE_HL2)
int size[2] = {3, 2};
pinfo->samples_len = 1024.0f;
#endif
int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / size[0];
int x = size[0] * (grid_offset % cell_per_row);
int y = size[1] * (grid_offset / cell_per_row);
#ifndef IRRADIANCE_SH_L2
/* Tweaking parameters to balance perf. vs precision */
const float bias = 0.0f;
pinfo->samples_len_inv = 1.0f / pinfo->samples_len;
pinfo->lodfactor = bias + 0.5f *
log((float)(target_size * target_size) * pinfo->samples_len_inv) /
log(2);
pinfo->lod_rt_max = floorf(log2f(target_size)) - 2.0f;
#else
pinfo->shres = 32; /* Less texture fetches & reduce branches */
pinfo->lod_rt_max = 2.0f; /* Improve cache reuse */
#endif
/* Start fresh */
GPU_framebuffer_ensure_config(&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE});
/* 4 - Compute diffuse irradiance */
EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max));
GPU_framebuffer_ensure_config(
&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, 0)});
GPU_framebuffer_bind(fb);
GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]);
DRW_draw_pass(psl->probe_diffuse_compute);
}
/* Filter rt_depth to light_cache->grid_tx.tex at index grid_offset */
void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
struct GPUTexture *UNUSED(rt_depth),
struct GPUFrameBuffer *fb,
int grid_offset,
float clipsta,
float clipend,
float vis_range,
float vis_blur,
int vis_size)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
LightCache *light_cache = vedata->stl->g_data->light_cache;
pinfo->samples_len = 512.0f; /* TODO refine */
pinfo->samples_len_inv = 1.0f / pinfo->samples_len;
pinfo->shres = vis_size;
pinfo->visibility_range = vis_range;
pinfo->visibility_blur = vis_blur;
pinfo->near_clip = -clipsta;
pinfo->far_clip = -clipend;
pinfo->texel_size = 1.0f / (float)vis_size;
int cell_per_col = GPU_texture_height(light_cache->grid_tx.tex) / vis_size;
int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / vis_size;
int x = vis_size * (grid_offset % cell_per_row);
int y = vis_size * ((grid_offset / cell_per_row) % cell_per_col);
int layer = 1 + ((grid_offset / cell_per_row) / cell_per_col);
GPU_framebuffer_ensure_config(
&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, layer)});
GPU_framebuffer_bind(fb);
GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size);
DRW_draw_pass(psl->probe_visibility_compute);
}
/* Actually a simple down-sampling. */
static void downsample_planar(void *vedata, int level)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
const float *size = DRW_viewport_size_get();
copy_v2_v2(stl->g_data->planar_texel_size, size);
for (int i = 0; i < level - 1; i++) {
stl->g_data->planar_texel_size[0] /= 2.0f;
stl->g_data->planar_texel_size[1] /= 2.0f;
min_ff(floorf(stl->g_data->planar_texel_size[0]), 1.0f);
min_ff(floorf(stl->g_data->planar_texel_size[1]), 1.0f);
}
invert_v2(stl->g_data->planar_texel_size);
DRW_draw_pass(psl->probe_planar_downsample_ps);
}
static void EEVEE_lightbake_filter_planar(EEVEE_Data *vedata)
{
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
DRW_stats_group_start("Planar Probe Downsample");
GPU_framebuffer_ensure_config(&fbl->planar_downsample_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->planar_pool)});
GPU_framebuffer_recursive_downsample(
fbl->planar_downsample_fb, MAX_PLANAR_LOD_LEVEL, &downsample_planar, vedata);
DRW_stats_group_end();
}
/** \} */
void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
if (pinfo->num_planar == 0) {
/* Disable SSR if we cannot read previous frame */
common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer;
common_data->prb_num_planar = 0;
return;
}
/* Temporary Remove all planar reflections (avoid lag effect). */
common_data->prb_num_planar = 0;
/* Turn off ssr to avoid black specular */
common_data->ssr_toggle = false;
common_data->ssrefract_toggle = false;
common_data->sss_toggle = false;
common_data->ray_type = EEVEE_RAY_GLOSSY;
common_data->ray_depth = 1.0f;
DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
/* Rendering happens here! */
eevee_lightbake_render_scene_to_planars(sldata, vedata);
/* Make sure no additional visibility check runs after this. */
pinfo->vis_data.collection = NULL;
DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data);
/* Restore */
common_data->prb_num_planar = pinfo->num_planar;
common_data->ssr_toggle = true;
common_data->ssrefract_toggle = true;
common_data->sss_toggle = true;
/* Prefilter for SSR */
if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) {
EEVEE_lightbake_filter_planar(vedata);
}
if (DRW_state_is_image_render()) {
/* Sort transparents because planar reflections could have re-sorted them. */
DRW_pass_sort_shgroup_z(vedata->psl->transparent_pass);
}
/* Disable SSR if we cannot read previous frame */
common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer;
}
void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
LightCache *light_cache = vedata->stl->g_data->light_cache;
if ((light_cache->flag & LIGHTCACHE_UPDATE_WORLD) &&
(light_cache->flag & LIGHTCACHE_BAKED) == 0) {
EEVEE_lightbake_update_world_quick(sldata, vedata, scene_eval);
}
}
void EEVEE_lightprobes_free(void)
{
MEM_SAFE_FREE(e_data.format_probe_display_cube);
MEM_SAFE_FREE(e_data.format_probe_display_planar);
DRW_TEXTURE_FREE_SAFE(e_data.hammersley);
DRW_TEXTURE_FREE_SAFE(e_data.planar_pool_placeholder);
DRW_TEXTURE_FREE_SAFE(e_data.depth_placeholder);
DRW_TEXTURE_FREE_SAFE(e_data.depth_array_placeholder);
}