Eevee: Irradiance Visibility: Initial Implementation

This augment the existing irradiance grid with a new visibility precomputation.
We store a small shadowmap for each grid sample so that light does not leak through walls and such.

The visibility parameter are similar to the one used by the Variance Shadow Map for point lights.

Technical details:

We store the visibility in the same texture (array) as the irradiance itself (in order to reduce the number of sampler).
But the irradiance and the visibility are not the same data so we must encode them in order to use the same texture format.
We use RGBA8 normalized texture and encode irradiance as RGBE (shared exponent).
Using RGBE encoding instead of R11_G11_B10 may lead to some lighting changes, but quality seems to be nearly the same in my test cases.
Using full RGBA16/32F maybe a future option but that will require much more memory and reduce the perf significantly.

Visibility moments (VSM) are encoded as 16bits fixed point precision using a special range. This seems to retain enough precision for the needs.
Also interpolation does not seems to be big problem (even though it's incorrect).
This commit is contained in:
Clément Foucault 2017-12-02 14:28:29 +01:00
parent 847f568bf5
commit 5b6cfa705c
14 changed files with 358 additions and 56 deletions

View File

@ -137,6 +137,7 @@ data_to_c_simple(engines/eevee/shaders/lamps_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_cube_display_frag.glsl SRC)

View File

@ -55,6 +55,7 @@ static void eevee_view_layer_data_free(void *storage)
DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_fb);
DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_filter_fb);
DRW_TEXTURE_FREE_SAFE(sldata->probe_rt);
DRW_TEXTURE_FREE_SAFE(sldata->probe_depth_rt);
DRW_TEXTURE_FREE_SAFE(sldata->probe_pool);
DRW_TEXTURE_FREE_SAFE(sldata->irradiance_pool);
DRW_TEXTURE_FREE_SAFE(sldata->irradiance_rt);

View File

@ -47,11 +47,13 @@
#include "ED_screen.h"
#define IRRADIANCE_POOL_SIZE 1024
#define HAMMERSLEY_SIZE 1024
static struct {
struct GPUShader *probe_default_sh;
struct GPUShader *probe_filter_glossy_sh;
struct GPUShader *probe_filter_diffuse_sh;
struct GPUShader *probe_filter_visibility_sh;
struct GPUShader *probe_grid_fill_sh;
struct GPUShader *probe_grid_display_sh;
struct GPUShader *probe_planar_display_sh;
@ -62,7 +64,6 @@ static struct {
struct GPUTexture *planar_pool_placeholder;
struct GPUTexture *depth_placeholder;
struct GPUTexture *depth_array_placeholder;
struct GPUTexture *cube_face_depth;
struct GPUTexture *cube_face_minmaxz;
int update_world;
@ -73,6 +74,7 @@ extern char datatoc_background_vert_glsl[];
extern char datatoc_default_world_frag_glsl[];
extern char datatoc_lightprobe_filter_glossy_frag_glsl[];
extern char datatoc_lightprobe_filter_diffuse_frag_glsl[];
extern char datatoc_lightprobe_filter_visibility_frag_glsl[];
extern char datatoc_lightprobe_geom_glsl[];
extern char datatoc_lightprobe_vert_glsl[];
extern char datatoc_lightprobe_planar_display_frag_glsl[];
@ -159,6 +161,7 @@ static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref)
void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(vedata))
{
bool update_all = false;
const DRWContextState *draw_ctx = DRW_context_state_get();
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);
@ -176,7 +179,7 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(veda
e_data.probe_filter_glossy_sh = DRW_shader_create(
datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, shader_str,
"#define HAMMERSLEY_SIZE 1024\n"
"#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n"
"#define NOISE_SIZE 64\n");
e_data.probe_default_sh = DRW_shader_create(
@ -200,7 +203,21 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(veda
#elif defined(IRRADIANCE_HL2)
"#define IRRADIANCE_HL2\n"
#endif
"#define HAMMERSLEY_SIZE 1024\n"
"#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n"
"#define NOISE_SIZE 64\n");
MEM_freeN(shader_str);
ds_frag = BLI_dynstr_new();
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_bsdf_sampling_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_lightprobe_filter_visibility_frag_glsl);
shader_str = BLI_dynstr_get_cstring(ds_frag);
BLI_dynstr_free(ds_frag);
e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen(
shader_str,
"#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n"
"#define NOISE_SIZE 64\n");
MEM_freeN(shader_str);
@ -260,7 +277,7 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(veda
datatoc_lightprobe_planar_downsample_frag_glsl,
NULL);
e_data.hammersley = create_hammersley_sample_texture(1024);
e_data.hammersley = create_hammersley_sample_texture(HAMMERSLEY_SIZE);
}
if (!sldata->probes) {
@ -275,21 +292,15 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(veda
}
int prop_bounce_num = BKE_collection_engine_property_value_get_int(props, "gi_diffuse_bounces");
/* Update all probes if number of bounces mismatch. */
if (sldata->probes->num_bounce != prop_bounce_num) {
e_data.update_world |= PROBE_UPDATE_ALL;
sldata->probes->updated_bounce = 0;
sldata->probes->grid_initialized = false;
sldata->probes->num_bounce = prop_bounce_num;
update_all = true;
}
sldata->probes->num_bounce = prop_bounce_num;
int prop_cubemap_res = BKE_collection_engine_property_value_get_int(props, "gi_cubemap_resolution");
if (sldata->probes->cubemap_res != prop_cubemap_res) {
sldata->probes->cubemap_res = prop_cubemap_res;
e_data.update_world |= PROBE_UPDATE_ALL;
sldata->probes->updated_bounce = 0;
sldata->probes->grid_initialized = false;
update_all = true;
sldata->probes->target_size = prop_cubemap_res >> 1;
@ -297,25 +308,27 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(veda
DRW_TEXTURE_FREE_SAFE(sldata->probe_pool);
}
/* Setup Render Target Cubemap */
/* We do this detach / attach dance to not generate an invalid framebuffer (mixed cubemap / 2D map) */
if (sldata->probe_rt) {
/* XXX Silly,TODO Cleanup this mess */
DRW_framebuffer_texture_detach(sldata->probe_rt);
int visibility_res = BKE_collection_engine_property_value_get_int(props, "gi_visibility_resolution");
if (sldata->probes->irradiance_vis_size != visibility_res) {
sldata->probes->irradiance_vis_size = visibility_res;
update_all = true;
}
DRWFboTexture tex_probe = {&e_data.cube_face_depth, DRW_TEX_DEPTH_24, DRW_TEX_TEMP};
DRW_framebuffer_init(&sldata->probe_fb, &draw_engine_eevee_type, sldata->probes->target_size, sldata->probes->target_size, &tex_probe, 1);
if (update_all) {
e_data.update_world |= PROBE_UPDATE_ALL;
sldata->probes->updated_bounce = 0;
sldata->probes->grid_initialized = false;
}
/* Setup Render Target Cubemap */
if (!sldata->probe_rt) {
sldata->probe_depth_rt = DRW_texture_create_cube(sldata->probes->target_size, DRW_TEX_DEPTH_24, 0, NULL);
sldata->probe_rt = DRW_texture_create_cube(sldata->probes->target_size, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
}
if (sldata->probe_rt) {
/* XXX Silly,TODO Cleanup this mess */
DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_rt, 0, 0);
}
DRWFboTexture tex_probe[2] = {{&sldata->probe_depth_rt, DRW_TEX_DEPTH_24, 0},
{&sldata->probe_rt, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}};
DRW_framebuffer_init(&sldata->probe_fb, &draw_engine_eevee_type, sldata->probes->target_size, sldata->probes->target_size, tex_probe, 2);
/* Minmaxz Pyramid */
// DRWFboTexture tex_minmaxz = {&e_data.cube_face_minmaxz, DRW_TEX_RG_32, DRW_TEX_MIPMAP | DRW_TEX_TEMP};
@ -434,11 +447,30 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat
DRW_shgroup_call_add(grp, geom, NULL);
}
{
psl->probe_visibility_compute = DRW_pass_create("LightProbe Visibility Compute", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_filter_visibility_sh, psl->probe_visibility_compute);
DRW_shgroup_uniform_int(grp, "outputSize", &sldata->probes->shres, 1);
DRW_shgroup_uniform_float(grp, "visibilityRange", &sldata->probes->visibility_range, 1);
DRW_shgroup_uniform_float(grp, "visibilityBlur", &sldata->probes->visibility_blur, 1);
DRW_shgroup_uniform_float(grp, "sampleCount", &sldata->probes->samples_ct, 1);
DRW_shgroup_uniform_float(grp, "invSampleCount", &sldata->probes->invsamples_ct, 1);
DRW_shgroup_uniform_float(grp, "storedTexelSize", &sldata->probes->texel_size, 1);
DRW_shgroup_uniform_float(grp, "nearClip", &sldata->probes->near_clip, 1);
DRW_shgroup_uniform_float(grp, "farClip", &sldata->probes->far_clip, 1);
DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
DRW_shgroup_uniform_texture(grp, "probeDepth", sldata->probe_depth_rt);
struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call_add(grp, geom, NULL);
}
{
psl->probe_grid_fill = DRW_pass_create("LightProbe Grid Floodfill", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_grid_fill_sh, psl->probe_grid_fill);
DRW_shgroup_uniform_buffer(grp, "gridTexture", &sldata->irradiance_pool);
DRW_shgroup_uniform_buffer(grp, "irradianceGrid", &sldata->irradiance_pool);
struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call_add(grp, geom, NULL);
@ -751,6 +783,13 @@ static void EEVEE_lightprobes_updates(EEVEE_ViewLayerData *sldata, EEVEE_PassLis
copy_v3_v3_int(egrid->resolution, &probe->grid_resolution_x);
/* Visibility bias */
egrid->visibility_bias = 0.05f * probe->vis_bias;
egrid->visibility_bleed = probe->vis_bleedbias;
egrid->visibility_range = max_ff(max_ff(len_v3(egrid->increment_x),
len_v3(egrid->increment_y)),
len_v3(egrid->increment_z)) + 1.0f;
/* Debug Display */
if (BKE_object_is_visible(ob) &&
DRW_state_draw_support() &&
@ -825,16 +864,18 @@ void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ved
/* we need a signed format for Spherical Harmonics */
int irradiance_format = DRW_TEX_RGBA_16;
#else
int irradiance_format = DRW_TEX_RGB_11_11_10;
int irradiance_format = DRW_TEX_RGBA_8;
#endif
/* TODO Allocate bigger storage if needed. */
if (!sldata->irradiance_pool || !sldata->irradiance_rt) {
if (!sldata->irradiance_pool) {
sldata->irradiance_pool = DRW_texture_create_2D(IRRADIANCE_POOL_SIZE, IRRADIANCE_POOL_SIZE, irradiance_format, DRW_TEX_FILTER, NULL);
sldata->irradiance_pool = DRW_texture_create_2D_array(IRRADIANCE_POOL_SIZE, IRRADIANCE_POOL_SIZE, 2,
irradiance_format, DRW_TEX_FILTER, NULL);
}
if (!sldata->irradiance_rt) {
sldata->irradiance_rt = DRW_texture_create_2D(IRRADIANCE_POOL_SIZE, IRRADIANCE_POOL_SIZE, irradiance_format, DRW_TEX_FILTER, NULL);
sldata->irradiance_rt = DRW_texture_create_2D_array(IRRADIANCE_POOL_SIZE, IRRADIANCE_POOL_SIZE, 2,
irradiance_format, DRW_TEX_FILTER, NULL);
}
pinfo->num_render_grid = 0;
pinfo->updated_bounce = 0;
@ -953,7 +994,9 @@ static void glossy_filter_probe(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
}
/* Diffuse filter probe_rt to irradiance_pool at index probe_idx */
static void diffuse_filter_probe(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, EEVEE_PassList *psl, int offset)
static void diffuse_filter_probe(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, EEVEE_PassList *psl, int offset,
float clipsta, float clipend, float vis_range, float vis_blur)
{
EEVEE_LightProbesInfo *pinfo = sldata->probes;
@ -994,6 +1037,26 @@ static void diffuse_filter_probe(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
DRW_framebuffer_viewport_size(sldata->probe_filter_fb, x, y, size[0], size[1]);
DRW_draw_pass(psl->probe_diffuse_compute);
/* Compute visibility */
pinfo->samples_ct = 512.0f; /* TODO refine */
pinfo->invsamples_ct = 1.0f / pinfo->samples_ct;
pinfo->shres = pinfo->irradiance_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)pinfo->irradiance_vis_size;
cell_per_row = IRRADIANCE_POOL_SIZE / pinfo->irradiance_vis_size;
x = pinfo->irradiance_vis_size * (offset % cell_per_row);
y = pinfo->irradiance_vis_size * (offset / cell_per_row);
DRW_framebuffer_texture_detach(sldata->irradiance_rt);
DRW_framebuffer_texture_layer_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 1, 0);
DRW_framebuffer_viewport_size(sldata->probe_filter_fb, x, y, pinfo->irradiance_vis_size, sldata->probes->irradiance_vis_size);
DRW_draw_pass(psl->probe_visibility_compute);
/* reattach to have a valid framebuffer. */
DRW_framebuffer_texture_detach(sldata->irradiance_rt);
DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
@ -1044,8 +1107,8 @@ static void render_scene_to_probe(
/* Detach to rebind the right cubeface. */
DRW_framebuffer_bind(sldata->probe_fb);
DRW_framebuffer_texture_attach(sldata->probe_fb, e_data.cube_face_depth, 0, 0);
DRW_framebuffer_texture_detach(sldata->probe_rt);
DRW_framebuffer_texture_detach(sldata->probe_depth_rt);
for (int i = 0; i < 6; ++i) {
float viewmat[4][4], persmat[4][4];
float viewinv[4][4], persinv[4][4];
@ -1068,6 +1131,7 @@ static void render_scene_to_probe(
EEVEE_draw_shadows(sldata, psl);
DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_rt, 0, i, 0);
DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, i, 0);
DRW_framebuffer_viewport_size(sldata->probe_fb, 0, 0, pinfo->target_size, pinfo->target_size);
DRW_framebuffer_clear(false, true, false, NULL, 1.0);
@ -1078,7 +1142,7 @@ static void render_scene_to_probe(
DRW_draw_pass(psl->probe_background);
// EEVEE_create_minmax_buffer(vedata, e_data.cube_face_depth);
// EEVEE_create_minmax_buffer(vedata, sldata->probe_depth_rt);
/* Rebind Planar FB */
DRW_framebuffer_bind(sldata->probe_fb);
@ -1089,9 +1153,10 @@ static void render_scene_to_probe(
DRW_draw_pass(psl->sss_pass); /* Only output standard pass */
DRW_framebuffer_texture_detach(sldata->probe_rt);
DRW_framebuffer_texture_detach(sldata->probe_depth_rt);
}
DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_rt, 0, 0);
DRW_framebuffer_texture_detach(e_data.cube_face_depth);
DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, 0);
DRW_viewport_matrix_override_unset(DRW_MAT_PERS);
DRW_viewport_matrix_override_unset(DRW_MAT_PERSINV);
@ -1212,6 +1277,7 @@ static void render_world_to_probe(EEVEE_ViewLayerData *sldata, EEVEE_PassList *p
/* Detach to rebind the right cubeface. */
DRW_framebuffer_bind(sldata->probe_fb);
DRW_framebuffer_texture_detach(sldata->probe_rt);
DRW_framebuffer_texture_detach(sldata->probe_depth_rt);
for (int i = 0; i < 6; ++i) {
float viewmat[4][4], persmat[4][4];
float viewinv[4][4], persinv[4][4];
@ -1237,6 +1303,7 @@ static void render_world_to_probe(EEVEE_ViewLayerData *sldata, EEVEE_PassList *p
DRW_framebuffer_texture_detach(sldata->probe_rt);
}
DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_rt, 0, 0);
DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, 0);
DRW_viewport_matrix_override_unset(DRW_MAT_PERS);
DRW_viewport_matrix_override_unset(DRW_MAT_PERSINV);
@ -1276,7 +1343,7 @@ static void lightprobes_refresh_world(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
glossy_filter_probe(sldata, vedata, psl, 0);
}
if (e_data.update_world & PROBE_UPDATE_GRID) {
diffuse_filter_probe(sldata, vedata, psl, 0);
diffuse_filter_probe(sldata, vedata, psl, 0, 0.0, 0.0, 0.0, 0.0);
SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt);
DRW_framebuffer_texture_detach(sldata->probe_pool);
DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
@ -1465,7 +1532,8 @@ static void lightprobes_refresh_all_no_world(EEVEE_ViewLayerData *sldata, EEVEE_
DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data);
}
render_scene_to_probe(sldata, vedata, pos, prb->clipsta, prb->clipend);
diffuse_filter_probe(sldata, vedata, psl, egrid->offset + cell_id);
diffuse_filter_probe(sldata, vedata, psl, egrid->offset + cell_id,
prb->clipsta, prb->clipend, egrid->visibility_range, prb->vis_blur);
/* To see what is going on. */
SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt);
/* Restore */
@ -1543,6 +1611,7 @@ void EEVEE_lightprobes_free(void)
DRW_SHADER_FREE_SAFE(e_data.probe_default_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh);

View File

@ -374,6 +374,7 @@ static void add_standard_uniforms(
DRW_shgroup_uniform_buffer(shgrp, "probeCubes", &sldata->probe_pool);
DRW_shgroup_uniform_buffer(shgrp, "probePlanars", &vedata->txl->planar_pool);
DRW_shgroup_uniform_buffer(shgrp, "irradianceGrid", &sldata->irradiance_pool);
DRW_shgroup_uniform_int(shgrp, "irradianceVisibilitySize", &sldata->probes->irradiance_vis_size, 1);
DRW_shgroup_uniform_buffer(shgrp, "shadowTexture", &sldata->shadow_pool);
DRW_shgroup_uniform_int(shgrp, "outputSsrId", ssr_id, 1);
DRW_shgroup_uniform_vec4(shgrp, "aoParameters[0]", &vedata->stl->effects->ao_dist, 2);

View File

@ -141,6 +141,7 @@ typedef struct EEVEE_PassList {
struct DRWPass *probe_background;
struct DRWPass *probe_glossy_compute;
struct DRWPass *probe_diffuse_compute;
struct DRWPass *probe_visibility_compute;
struct DRWPass *probe_grid_fill;
struct DRWPass *probe_display;
struct DRWPass *probe_planar_downsample_ps;
@ -369,6 +370,7 @@ typedef struct EEVEE_LightGrid {
float increment_x[3], attenuation_bias; /* world space vector between 2 opposite cells */
float increment_y[3], level_bias;
float increment_z[3], pad4;
float visibility_bias, visibility_bleed, visibility_range, pad5;
} EEVEE_LightGrid;
typedef struct EEVEE_PlanarReflection {
@ -391,6 +393,7 @@ typedef struct EEVEE_LightProbesInfo {
int num_bounce;
int cubemap_res;
int target_size;
int irradiance_vis_size;
int grid_initialized;
/* Actual number of probes that have datas. */
int num_render_cube;
@ -402,9 +405,13 @@ typedef struct EEVEE_LightProbesInfo {
float padding_size;
float samples_ct;
float invsamples_ct;
float near_clip;
float far_clip;
float roughness;
float lodfactor;
float lod_rt_max, lod_cube_max, lod_planar_max;
float visibility_range;
float visibility_blur;
int shres;
int shnbr;
bool specular_toggle;
@ -550,6 +557,7 @@ typedef struct EEVEE_ViewLayerData {
struct GPUFrameBuffer *probe_filter_fb;
struct GPUTexture *probe_rt;
struct GPUTexture *probe_depth_rt;
struct GPUTexture *probe_pool;
struct GPUTexture *irradiance_pool;
struct GPUTexture *irradiance_rt;

View File

@ -86,8 +86,8 @@ static void eevee_create_shader_volumes(void)
ds_frag = BLI_dynstr_new();
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_lamps_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_volumetric_lib_glsl);
e_data.volumetric_common_lamps_lib = BLI_dynstr_get_cstring(ds_frag);

View File

@ -432,6 +432,78 @@ vec3 normal_decode(vec2 enc, vec3 view)
return n;
}
/* ---- RGBM (shared multiplier) encoding ---- */
/* From http://iwasbeingirony.blogspot.fr/2010/06/difference-between-rgbm-and-rgbd.html */
/* Higher RGBM_MAX_RANGE gives imprecision issues in low intensity. */
#define RGBM_MAX_RANGE 512.0
vec4 rgbm_encode(vec3 rgb)
{
float maxRGB = max_v3(rgb);
float M = maxRGB / RGBM_MAX_RANGE;
M = ceil(M * 255.0) / 255.0;
return vec4(rgb / (M * RGBM_MAX_RANGE), M);
}
vec3 rgbm_decode(vec4 data)
{
return data.rgb * (data.a * RGBM_MAX_RANGE);
}
/* ---- RGBE (shared exponent) encoding ---- */
vec4 rgbe_encode(vec3 rgb)
{
float maxRGB = max_v3(rgb);
float fexp = ceil(log2(maxRGB));
return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0);
}
vec3 rgbe_decode(vec4 data)
{
float fexp = data.a * 255.0 - 128.0;
return data.rgb * exp2(fexp);
}
#if 1
#define irradiance_encode rgbe_encode
#define irradiance_decode rgbe_decode
#else /* No ecoding (when using floating point format) */
#define irradiance_encode(X) (X).rgbb
#define irradiance_decode(X) (X).rgb
#endif
/* Irradiance Visibility Encoding */
#if 1
vec4 visibility_encode(vec2 accum, float range)
{
accum /= range;
vec4 data;
data.x = fract(accum.x);
data.y = floor(accum.x) / 255.0;
data.z = fract(accum.y);
data.w = floor(accum.y) / 255.0;
return data;
}
vec2 visibility_decode(vec4 data, float range)
{
return (data.xz + data.yw * 255.0) * range;
}
#else /* No ecoding (when using floating point format) */
vec4 visibility_encode(vec2 accum, float range)
{
return accum.xyxy;
}
vec2 visibility_decode(vec4 data, float range)
{
return data.xy;
}
#endif
/* Fresnel monochromatic, perfect mirror */
float F_eta(float eta, float cos_theta)
{

View File

@ -90,4 +90,18 @@ vec3 sample_hemisphere(float nsample, vec3 N, vec3 T, vec3 B)
return tangent_to_world(Ht, N, T, B);
}
vec3 sample_cone(float nsample, float angle, vec3 N, vec3 T, vec3 B)
{
vec3 Xi = hammersley_3d(nsample);
float z = cos(angle * Xi.x); /* cos theta */
float r = sqrt( 1.0f - z*z ); /* sin theta */
float x = r * Xi.y;
float y = r * Xi.z;
vec3 Ht = vec3(x, y, z);
return tangent_to_world(Ht, N, T, B);
}
#endif

View File

@ -1,5 +1,6 @@
uniform sampler2D irradianceGrid;
uniform sampler2DArray irradianceGrid;
uniform int irradianceVisibilitySize;
#define IRRADIANCE_LIB
@ -36,7 +37,7 @@ IrradianceData load_irradiance_cell(int cell, vec3 N)
uvs += vec2(cell_co) / vec2(textureSize(irradianceGrid, 0));
IrradianceData ir;
ir.color = texture(irradianceGrid, uvs).rgb;
ir.color = texture(irradianceGrid, vec3(uvs, 0.0)).rgb;
#elif defined(IRRADIANCE_SH_L2)
@ -48,15 +49,15 @@ IrradianceData load_irradiance_cell(int cell, vec3 N)
ivec3 ofs = ivec3(0, 1, 2);
IrradianceData ir;
ir.shcoefs[0] = texelFetch(irradianceGrid, cell_co + ofs.xx, 0).rgb;
ir.shcoefs[1] = texelFetch(irradianceGrid, cell_co + ofs.yx, 0).rgb;
ir.shcoefs[2] = texelFetch(irradianceGrid, cell_co + ofs.zx, 0).rgb;
ir.shcoefs[3] = texelFetch(irradianceGrid, cell_co + ofs.xy, 0).rgb;
ir.shcoefs[4] = texelFetch(irradianceGrid, cell_co + ofs.yy, 0).rgb;
ir.shcoefs[5] = texelFetch(irradianceGrid, cell_co + ofs.zy, 0).rgb;
ir.shcoefs[6] = texelFetch(irradianceGrid, cell_co + ofs.xz, 0).rgb;
ir.shcoefs[7] = texelFetch(irradianceGrid, cell_co + ofs.yz, 0).rgb;
ir.shcoefs[8] = texelFetch(irradianceGrid, cell_co + ofs.zz, 0).rgb;
ir.shcoefs[0] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xx, 0), 0).rgb;
ir.shcoefs[1] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yx, 0), 0).rgb;
ir.shcoefs[2] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zx, 0), 0).rgb;
ir.shcoefs[3] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xy, 0), 0).rgb;
ir.shcoefs[4] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yy, 0), 0).rgb;
ir.shcoefs[5] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zy, 0), 0).rgb;
ir.shcoefs[6] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xz, 0), 0).rgb;
ir.shcoefs[7] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yz, 0), 0).rgb;
ir.shcoefs[8] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zz, 0), 0).rgb;
#else /* defined(IRRADIANCE_HL2) */
@ -68,15 +69,50 @@ IrradianceData load_irradiance_cell(int cell, vec3 N)
ivec3 is_negative = ivec3(step(0.0, -N));
IrradianceData ir;
ir.cubesides[0] = texelFetch(irradianceGrid, cell_co + ivec2(0, is_negative.x), 0).rgb;
ir.cubesides[1] = texelFetch(irradianceGrid, cell_co + ivec2(1, is_negative.y), 0).rgb;
ir.cubesides[2] = texelFetch(irradianceGrid, cell_co + ivec2(2, is_negative.z), 0).rgb;
ir.cubesides[0] = irradiance_decode(texelFetch(irradianceGrid, ivec3(cell_co + ivec2(0, is_negative.x), 0), 0));
ir.cubesides[1] = irradiance_decode(texelFetch(irradianceGrid, ivec3(cell_co + ivec2(1, is_negative.y), 0), 0));
ir.cubesides[2] = irradiance_decode(texelFetch(irradianceGrid, ivec3(cell_co + ivec2(2, is_negative.z), 0), 0));
#endif
return ir;
}
float load_visibility_cell(int cell, vec3 L, float dist, float bias, float bleed_bias, float range)
{
/* Keep in sync with diffuse_filter_probe() */
ivec2 cell_co = ivec2(irradianceVisibilitySize);
int cell_per_row = textureSize(irradianceGrid, 0).x / irradianceVisibilitySize;
cell_co.x *= (cell) % cell_per_row;
cell_co.y *= (cell) / cell_per_row;
vec2 texel_size = 1.0 / vec2(textureSize(irradianceGrid, 0).xy);
vec2 co = vec2(cell_co) * texel_size;
vec2 uv = mapping_octahedron(-L, vec2(1.0 / float(irradianceVisibilitySize)));
uv *= vec2(irradianceVisibilitySize) * texel_size;
vec4 data = texture(irradianceGrid, vec3(co + uv, 1.0));
/* Decoding compressed data */
vec2 moments = visibility_decode(data, range);
/* Doing chebishev test */
float variance = abs(moments.x * moments.x - moments.y);
variance = max(variance, bias / 10.0);
float d = dist - moments.x;
float p_max = variance / (variance + d * d);
/* Increase contrast in the weight by squaring it */
p_max *= p_max;
/* Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] */
p_max = clamp((p_max - bleed_bias) / (1.0 - bleed_bias), 0.0, 1.0);
return (dist <= moments.x) ? 1.0 : p_max;
}
/* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */
vec3 spherical_harmonics_L1(vec3 N, vec3 shcoefs[4])
{

View File

@ -192,6 +192,6 @@ void main()
}
}
FragColor = vec4(out_radiance / weight, 1.0);
FragColor = irradiance_encode(out_radiance / weight);
#endif
}

View File

@ -0,0 +1,88 @@
uniform samplerCube probeDepth;
uniform int outputSize;
uniform float lodFactor;
uniform float storedTexelSize;
uniform float lodMax;
uniform float nearClip;
uniform float farClip;
uniform float visibilityRange;
uniform float visibilityBlur;
out vec4 FragColor;
vec3 octahedral_to_cubemap_proj(vec2 co)
{
co = co * 2.0 - 1.0;
vec2 abs_co = abs(co);
vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y));
if ( abs_co.x + abs_co.y > 1.0 ) {
v.xy = (abs(co.yx) - 1.0) * -sign(co.xy);
}
return v;
}
float linear_depth(float z)
{
return (nearClip * farClip) / (z * (nearClip - farClip) + farClip);
}
float get_world_distance(float depth, vec3 cos)
{
float is_background = step(1.0, depth);
depth = linear_depth(depth);
depth += 1e1 * is_background;
cos = normalize(abs(cos));
float cos_vec = max(cos.x, max(cos.y, cos.z));
return depth / cos_vec;
}
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy) % ivec2(outputSize);
vec3 cos;
cos.xy = (vec2(texel) + 0.5) * storedTexelSize;
/* add a 2 pixel border to ensure filtering is correct */
cos.xy *= 1.0 + storedTexelSize * 2.0;
cos.xy -= storedTexelSize;
float pattern = 1.0;
/* edge mirroring : only mirror if directly adjacent
* (not diagonally adjacent) */
vec2 m = abs(cos.xy - 0.5) + 0.5;
vec2 f = floor(m);
if (f.x - f.y != 0.0) {
cos.xy = 1.0 - cos.xy;
}
/* clamp to [0-1] */
cos.xy = fract(cos.xy);
/* get cubemap vector */
cos = normalize(octahedral_to_cubemap_proj(cos.xy));
vec3 T, B;
make_orthonormal_basis(cos, T, B); /* Generate tangent space */
vec2 accum = vec2(0.0);
for (float i = 0; i < sampleCount; i++) {
vec3 sample = sample_cone(i, M_PI_2 * visibilityBlur, cos, T, B);
float depth = texture(probeDepth, sample).r;
depth = get_world_distance(depth, sample);
accum += vec2(depth, depth * depth);
}
accum *= invSampleCount;
accum = abs(accum);
/* Encode to normalized RGBA 8 */
FragColor = visibility_encode(accum, visibilityRange);
}

View File

@ -1,4 +1,4 @@
uniform sampler2D gridTexture;
uniform sampler2DArray irradianceGrid;
out vec4 FragColor;
@ -12,7 +12,7 @@ void main()
const ivec2 data_size = ivec2(3, 2);
#endif
ivec2 coord = ivec2(gl_FragCoord.xy) % data_size;
FragColor = texelFetch(gridTexture, coord, 0);
FragColor = texelFetch(irradianceGrid, ivec3(coord, 0), 0);
if (any(greaterThanEqual(ivec2(gl_FragCoord.xy), data_size))) {
FragColor = vec4(0.0, 0.0, 0.0, 1.0);

View File

@ -49,6 +49,7 @@ struct GridData {
vec4 ws_increment_x_atten_bias; /* world space vector between 2 opposite cells */
vec4 ws_increment_y_lvl_bias;
vec4 ws_increment_z;
vec4 vis_bias_bleed_range;
};
#define g_corner ws_corner_atten_scale.xyz
@ -60,6 +61,9 @@ struct GridData {
#define g_increment_z ws_increment_z.xyz
#define g_resolution resolution_offset.xyz
#define g_offset resolution_offset.w
#define g_vis_bias vis_bias_bleed_range.x
#define g_vis_bleed vis_bias_bleed_range.y
#define g_vis_range vis_bias_bleed_range.z
#ifndef MAX_PROBE
#define MAX_PROBE 1
@ -227,14 +231,18 @@ vec3 probe_evaluate_grid(GridData gd, vec3 W, vec3 N, vec3 localpos)
gd.g_increment_y * cell_cos.y +
gd.g_increment_z * cell_cos.z);
// vec3 ws_point_to_cell = ws_cell_location - W;
// vec3 ws_light = normalize(ws_point_to_cell);
vec3 ws_point_to_cell = ws_cell_location - W;
float ws_dist_point_to_cell = length(ws_point_to_cell);
vec3 ws_light = ws_point_to_cell / ws_dist_point_to_cell;
vec3 trilinear = mix(1 - trilinear_weight, trilinear_weight, offset);
float weight = trilinear.x * trilinear.y * trilinear.z;
/* Precomputed visibility */
weight *= load_visibility_cell(cell, ws_light, ws_dist_point_to_cell, gd.g_vis_bias, gd.g_vis_bleed, gd.g_vis_range);
/* Smooth backface test */
// weight *= sqrt(max(0.002, dot(ws_light, N)));
weight *= sqrt(max(0.002, dot(ws_light, N)));
/* Avoid zero weight */
weight = max(0.00001, weight);

View File

@ -128,12 +128,16 @@ vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D v
#ifdef IRRADIANCE_LIB
vec3 irradiance_volumetric(vec3 wpos)
{
#ifdef IRRADIANCE_HL2
IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0));
vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
ir_data = load_irradiance_cell(0, vec3(-1.0));
irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
irradiance *= 0.16666666; /* 1/6 */
return irradiance;
#else
return vec3(0.0);
#endif
}
#endif