This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/draw/engines/eevee/eevee_lightprobes.c
Clément Foucault b18c2a3c41 EEVEE: Refactor of eevee_material.c
These are the modifications:

-With DRW modification we reduce the number of passes we need to populate.
-Rename passes for consistent naming.
-Reduce complexity in code compilation
-Cleanup how renderpass accumulation passes are setup, using pass instances.
-Make sculpt mode compatible with shadows
-Make hair passes compatible with SSS
-Error shader and lookdev materials now use standalone materials.
-Support default shader (world and material) using a default nodetree internally.
-Change BLEND_CLIP to be emulated by gpu nodetree. Making less shader variations.
-Use BLI_memblock for cache memory allocation.
-Renderpasses are handled by switching a UBO ref bind.

One major hack in this patch is the use of modified pointer as ghash keys.
This rely on the assumption that the keys will never overlap because the
number of options per key will never be bigger than the pointed struct.

The use of one single nodetree to support default material is also a bit hacky
since it won't support concurent usage of this nodetree.
(see EEVEE_shader_default_surface_nodetree)

Another change is that objects with shader errors now appear solid magenta instead
of shaded magenta. This is only because of code reuse purpose but could be changed
if really needed.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D7642
2020-06-02 16:58:07 +02:00

1301 lines
49 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_rand.h"
#include "BLI_utildefines.h"
#include "DNA_image_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_texture_types.h"
#include "DNA_view3d_types.h"
#include "DNA_world_types.h"
#include "BKE_collection.h"
#include "BKE_object.h"
#include "MEM_guardedalloc.h"
#include "GPU_extensions.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 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));
if (EEVEE_lightcache_load(scene_eval->eevee.light_cache_data)) {
stl->g_data->light_cache = scene_eval->eevee.light_cache_data;
}
else {
if (!sldata->fallback_lightcache) {
#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
sldata->fallback_lightcache = EEVEE_lightcache_create(
1,
1,
scene_eval->eevee.gi_cubemap_resolution,
scene_eval->eevee.gi_visibility_resolution,
(int[3]){grid_res, grid_res, 1});
}
stl->g_data->light_cache = sldata->fallback_lightcache;
}
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);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call_instances(grp, NULL, geom, 6);
}
{
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);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
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);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
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;
/* LookDev */
EEVEE_lookdev_cache_init(vedata, sldata, &grp, psl->probe_background, wo, pinfo);
if (!grp && wo) {
struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_WORLD_PROBE);
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_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call(grp, geom, NULL);
}
/* 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", G_draw.block.colorBackground, 1);
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
DRW_shgroup_call(grp, geom, NULL);
}
}
if (DRW_state_draw_support()) {
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);
if (!LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) {
/* 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", 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_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
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", 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);
DRW_shgroup_uniform_block(shgrp, "renderpass_block", sldata->renderpass_ubo.combined);
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);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
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 >= EEVEE_PROBE_MAX) ||
(probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= EEVEE_PROBE_MAX) ||
(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;
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_f(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_data != NULL) {
if (pinfo->do_grid_update) {
scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_GRID;
}
/* If we update grid we need to update the cube-maps too.
* So always refresh cube-maps. */
scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_CUBE;
/* Tag the lightcache to auto update. */
scene_orig->eevee.light_cache_data->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_ps);
DRW_draw_pass(psl->probe_background);
DRW_draw_pass(psl->material_ps);
DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */
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_ps);
DRW_draw_pass(psl->depth_refract_ps);
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 */
DRW_draw_pass(psl->material_ps);
DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */
DRW_draw_pass(psl->material_refract_ps);
/* 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 = log2_floor_u(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 * 6;
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 = log2_floor_u(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);
}