Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2017-08-18 16:06:51 +0300
committerClément Foucault <foucault.clem@gmail.com>2017-08-18 16:09:43 +0300
commit659be38760784b51cf17c768cdf74cdd5718ba71 (patch)
tree2e952a814048abed97058f341bf9be72747249f3
parent25789f24f2d6c6076e8b3d04a12a1a29c2732ba4 (diff)
Eevee: Rework GTAO
This includes big improvement: - The horizon search is decoupled from the BSDF evaluation. This means using multiple BSDF nodes have a much lower impact when enbaling AO. - The horizon search is optimized by splitting the search into 4 corners searching similar directions to help which GPU cache coherence. - The AO options are now uniforms and do not trigger shader recompilation (aka. freeze UI). - Include a quality slider similar to the SSR one. - Add a switch for disabling bounce light approximation. - Fix problem with Bent Normals when occlusion get very dark. - Add a denoise option to that takes the neighbors pixel values via glsl derivatives. This reduces noise but exhibit 2x2 blocky artifacts. The downside : Separating the horizon search uses more memory (~3MB for each samples on HD viewport). We could lower the bit depth to 4bit per horizon but it produce noticeable banding (might be fixed with some dithering).
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_render_layer.py3
-rw-r--r--source/blender/draw/CMakeLists.txt1
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c200
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c18
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c6
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c49
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h27
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl302
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl69
-rw-r--r--source/blender/makesrna/intern/rna_scene.c27
11 files changed, 546 insertions, 159 deletions
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index e6dbd8c8eab..5ef1424ca0f 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -657,9 +657,12 @@ class RENDER_PT_eevee_postprocess_settings(RenderButtonsPanel, Panel):
col.label("Ambient Occlusion:")
col.prop(props, "gtao_use_bent_normals")
+ col.prop(props, "gtao_denoise")
+ col.prop(props, "gtao_bounce")
col.prop(props, "gtao_samples")
col.prop(props, "gtao_distance")
col.prop(props, "gtao_factor")
+ col.prop(props, "gtao_quality")
col.separator()
col.label("Motion Blur:")
diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py
index edbaddc4f6c..06598db2650 100644
--- a/release/scripts/startup/bl_ui/properties_render_layer.py
+++ b/release/scripts/startup/bl_ui/properties_render_layer.py
@@ -192,9 +192,12 @@ class RENDERLAYER_PT_eevee_postprocess_settings(RenderLayerButtonsPanel, Panel):
col = layout.column()
col.label("Ambient Occlusion:")
col.template_override_property(layer_props, scene_props, "gtao_use_bent_normals")
+ col.template_override_property(layer_props, scene_props, "gtao_denoise")
+ col.template_override_property(layer_props, scene_props, "gtao_bounce")
col.template_override_property(layer_props, scene_props, "gtao_samples")
col.template_override_property(layer_props, scene_props, "gtao_distance")
col.template_override_property(layer_props, scene_props, "gtao_factor")
+ col.template_override_property(layer_props, scene_props, "gtao_quality")
col.separator()
col.label("Motion Blur:")
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index d5e4a402330..2a22715baf3 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -141,6 +141,7 @@ data_to_c_simple(engines/eevee/shaders/effect_dof_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_downsample_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/effect_gtao_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_motion_blur_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_ssr_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl SRC)
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index 2922e4622ad..f16393eb35a 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -99,11 +99,16 @@ static struct {
/* Simple Downsample */
struct GPUShader *downsample_sh;
+ /* Ground Truth Ambient Occlusion */
+ struct GPUShader *gtao_sh;
+ struct GPUShader *gtao_debug_sh;
+
struct GPUTexture *depth_src;
struct GPUTexture *color_src;
int depth_src_layer;
} e_data = {NULL}; /* Engine data */
+extern char datatoc_ambient_occlusion_lib_glsl[];
extern char datatoc_bsdf_common_lib_glsl[];
extern char datatoc_bsdf_sampling_lib_glsl[];
extern char datatoc_octahedron_lib_glsl[];
@@ -115,6 +120,7 @@ extern char datatoc_effect_dof_vert_glsl[];
extern char datatoc_effect_dof_geom_glsl[];
extern char datatoc_effect_dof_frag_glsl[];
extern char datatoc_effect_downsample_frag_glsl[];
+extern char datatoc_effect_gtao_frag_glsl[];
extern char datatoc_lightprobe_lib_glsl[];
extern char datatoc_raytrace_lib_glsl[];
extern char datatoc_tonemap_frag_glsl[];
@@ -229,6 +235,18 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
/* Shaders */
if (!e_data.motion_blur_sh) {
+ DynStr *ds_frag = BLI_dynstr_new();
+ BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
+ BLI_dynstr_append(ds_frag, datatoc_ambient_occlusion_lib_glsl);
+ BLI_dynstr_append(ds_frag, datatoc_effect_gtao_frag_glsl);
+ char *frag_str = BLI_dynstr_get_cstring(ds_frag);
+ BLI_dynstr_free(ds_frag);
+
+ e_data.gtao_sh = DRW_shader_create_fullscreen(frag_str, NULL);
+ e_data.gtao_debug_sh = DRW_shader_create_fullscreen(frag_str, "#define DEBUG_AO\n");
+
+ MEM_freeN(frag_str);
+
e_data.downsample_sh = DRW_shader_create_fullscreen(datatoc_effect_downsample_frag_glsl, NULL);
e_data.volumetric_upsample_sh = DRW_shader_create_fullscreen(datatoc_volumetric_frag_glsl, "#define STEP_UPSAMPLE\n");
@@ -479,11 +497,68 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
&tex, 1);
}
- {
+ if (BKE_collection_engine_property_value_get_bool(props, "gtao_enable")) {
/* Ambient Occlusion*/
- stl->effects->ao_dist = BKE_collection_engine_property_value_get_float(props, "gtao_distance");
- stl->effects->ao_samples = BKE_collection_engine_property_value_get_int(props, "gtao_samples");
- stl->effects->ao_factor = BKE_collection_engine_property_value_get_float(props, "gtao_factor");
+ effects->enabled_effects |= EFFECT_GTAO;
+
+ effects->ao_dist = BKE_collection_engine_property_value_get_float(props, "gtao_distance");
+ effects->ao_factor = BKE_collection_engine_property_value_get_float(props, "gtao_factor");
+ effects->ao_quality = 1.0f - BKE_collection_engine_property_value_get_float(props, "gtao_quality");
+ effects->ao_samples = BKE_collection_engine_property_value_get_int(props, "gtao_samples");
+ effects->ao_samples_inv = 1.0f / effects->ao_samples;
+
+ effects->ao_settings = 1.0; /* USE_AO */
+ if (BKE_collection_engine_property_value_get_bool(props, "gtao_use_bent_normals")) {
+ effects->ao_settings += 2.0; /* USE_BENT_NORMAL */
+ }
+ if (BKE_collection_engine_property_value_get_bool(props, "gtao_denoise")) {
+ effects->ao_settings += 4.0; /* USE_DENOISE */
+ }
+
+ effects->ao_offset = 0.0f;
+ effects->ao_bounce_fac = (float)BKE_collection_engine_property_value_get_bool(props, "gtao_bounce");
+
+ effects->ao_texsize[0] = ((int)viewport_size[0]);
+ effects->ao_texsize[1] = ((int)viewport_size[1]);
+
+ /* Round up to multiple of 2 */
+ if ((effects->ao_texsize[0] & 0x1) != 0) {
+ effects->ao_texsize[0] += 1;
+ }
+ if ((effects->ao_texsize[1] & 0x1) != 0) {
+ effects->ao_texsize[1] += 1;
+ }
+
+ CLAMP(effects->ao_samples, 1, 32);
+
+ if (effects->hori_tex_layers != effects->ao_samples) {
+ DRW_TEXTURE_FREE_SAFE(txl->gtao_horizons);
+ }
+
+ if (txl->gtao_horizons == NULL) {
+ effects->hori_tex_layers = effects->ao_samples;
+ txl->gtao_horizons = DRW_texture_create_2D_array((int)viewport_size[0], (int)viewport_size[1], effects->hori_tex_layers, DRW_TEX_RG_8, 0, NULL);
+ }
+
+ DRWFboTexture tex = {&txl->gtao_horizons, DRW_TEX_RG_8, 0};
+
+ DRW_framebuffer_init(&fbl->gtao_fb, &draw_engine_eevee_type,
+ effects->ao_texsize[0], effects->ao_texsize[1],
+ &tex, 1);
+
+ if (G.debug_value == 6) {
+ DRWFboTexture tex_debug = {&stl->g_data->gtao_horizons_debug, DRW_TEX_RGBA_8, DRW_TEX_TEMP};
+
+ DRW_framebuffer_init(&fbl->gtao_debug_fb, &draw_engine_eevee_type,
+ (int)viewport_size[0], (int)viewport_size[1],
+ &tex_debug, 1);
+ }
+ }
+ else {
+ /* Cleanup */
+ DRW_TEXTURE_FREE_SAFE(txl->gtao_horizons);
+ DRW_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb);
+ effects->ao_settings = 0.0f;
}
/* MinMax Pyramid */
@@ -493,6 +568,11 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
(int)viewport_size[0] / 2, (int)viewport_size[1] / 2,
&texmin, 1);
+ /* Cannot define 2 depth texture for one framebuffer. So allocate ourself. */
+ if (txl->maxzbuffer == NULL) {
+ txl->maxzbuffer = DRW_texture_create_2D((int)viewport_size[0] / 2, (int)viewport_size[1] / 2, DRW_TEX_DEPTH_24, DRW_TEX_MIPMAP, NULL);
+ }
+
/* Compute Mipmap texel alignement. */
for (int i = 0; i < 10; ++i) {
float mip_size[2] = {viewport_size[0], viewport_size[1]};
@@ -504,11 +584,6 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
stl->g_data->mip_ratio[i][1] = viewport_size[1] / (mip_size[1] * powf(2.0f, floorf(log2f(floorf(viewport_size[1] / mip_size[1])))));
}
- /* Cannot define 2 depth texture for one framebuffer. So allocate ourself. */
- if (txl->maxzbuffer == NULL) {
- txl->maxzbuffer = DRW_texture_create_2D((int)viewport_size[0] / 2, (int)viewport_size[1] / 2, DRW_TEX_DEPTH_24, DRW_TEX_MIPMAP, NULL);
- }
-
if (BKE_collection_engine_property_value_get_bool(props, "volumetric_enable")) {
World *wo = scene->world;
@@ -617,11 +692,6 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
/* MRT for the shading pass in order to output needed data for the SSR pass. */
/* TODO create one texture layer per lobe */
- if (txl->ssr_normal_input == NULL) {
- DRWTextureFormat nor_format = DRW_TEX_RG_16;
- txl->ssr_normal_input = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1], nor_format, 0, NULL);
- }
-
if (txl->ssr_specrough_input == NULL) {
DRWTextureFormat specrough_format = (high_qual_input) ? DRW_TEX_RGBA_16 : DRW_TEX_RGBA_8;
txl->ssr_specrough_input = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1], specrough_format, 0, NULL);
@@ -629,9 +699,7 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
/* Reattach textures to the right buffer (because we are alternating between buffers) */
/* TODO multiple FBO per texture!!!! */
- DRW_framebuffer_texture_detach(txl->ssr_normal_input);
DRW_framebuffer_texture_detach(txl->ssr_specrough_input);
- DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0);
/* Raytracing output */
@@ -649,7 +717,6 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
}
else {
/* Cleanup to release memory */
- DRW_TEXTURE_FREE_SAFE(txl->ssr_normal_input);
DRW_TEXTURE_FREE_SAFE(txl->ssr_specrough_input);
DRW_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb);
for (int i = 0; i < 4; ++i) {
@@ -657,6 +724,25 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
}
}
+ /* Normal buffer for deferred passes. */
+ if ((((effects->enabled_effects & EFFECT_GTAO) != 0) && G.debug_value == 6) ||
+ ((effects->enabled_effects & EFFECT_SSR) != 0))
+ {
+ if (txl->ssr_normal_input == NULL) {
+ DRWTextureFormat nor_format = DRW_TEX_RG_16;
+ txl->ssr_normal_input = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1], nor_format, 0, NULL);
+ }
+
+ /* Reattach textures to the right buffer (because we are alternating between buffers) */
+ /* TODO multiple FBO per texture!!!! */
+ DRW_framebuffer_texture_detach(txl->ssr_normal_input);
+ DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
+ }
+ else {
+ /* Cleanup to release memory */
+ DRW_TEXTURE_FREE_SAFE(txl->ssr_normal_input);
+ }
+
/* Setup double buffer so we can access last frame as it was before post processes */
if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) {
DRWFboTexture tex_double_buffer = {&txl->color_double_buffer, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP};
@@ -867,6 +953,33 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
}
{
+ psl->ao_horizon_search = DRW_pass_create("GTAO Horizon Search", DRW_STATE_WRITE_COLOR);
+ DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_sh, psl->ao_horizon_search);
+ DRW_shgroup_uniform_buffer(grp, "maxzBuffer", &txl->maxzbuffer);
+ DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
+ DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2);
+ DRW_shgroup_uniform_vec2(grp, "mipRatio[0]", (float *)stl->g_data->mip_ratio, 10);
+ DRW_shgroup_uniform_vec4(grp, "aoParameters[0]", &stl->effects->ao_dist, 2);
+ DRW_shgroup_uniform_float(grp, "sampleNbr", &stl->effects->ao_sample_nbr, 1);
+ DRW_shgroup_uniform_ivec2(grp, "aoHorizonTexSize", (int *)stl->effects->ao_texsize, 1);
+ DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
+ DRW_shgroup_call_add(grp, quad, NULL);
+
+ psl->ao_horizon_debug = DRW_pass_create("GTAO Horizon Debug", DRW_STATE_WRITE_COLOR);
+ grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_horizon_debug);
+ DRW_shgroup_uniform_buffer(grp, "maxzBuffer", &txl->maxzbuffer);
+ DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
+ DRW_shgroup_uniform_buffer(grp, "normalBuffer", &txl->ssr_normal_input);
+ DRW_shgroup_uniform_buffer(grp, "horizonBuffer", &txl->gtao_horizons);
+ DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2);
+ DRW_shgroup_uniform_vec2(grp, "mipRatio[0]", (float *)stl->g_data->mip_ratio, 10);
+ DRW_shgroup_uniform_vec4(grp, "aoParameters[0]", &stl->effects->ao_dist, 2);
+ DRW_shgroup_uniform_ivec2(grp, "aoHorizonTexSize", (int *)stl->effects->ao_texsize, 1);
+ DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
+ DRW_shgroup_call_add(grp, quad, NULL);
+ }
+
+ {
psl->motion_blur = DRW_pass_create("Motion Blur", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.motion_blur_sh, psl->motion_blur);
@@ -1138,6 +1251,49 @@ void EEVEE_effects_do_ssr(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *veda
DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0);
}
+
+ if ((effects->enabled_effects & EFFECT_GTAO) != 0 && G.debug_value == 6) {
+ /* GTAO Debug */
+ DRW_framebuffer_texture_attach(fbl->gtao_debug_fb, stl->g_data->gtao_horizons_debug, 0, 0);
+ DRW_framebuffer_bind(fbl->gtao_debug_fb);
+
+ DRW_draw_pass(psl->ao_horizon_debug);
+
+ /* Restore */
+ DRW_framebuffer_texture_detach(stl->g_data->gtao_horizons_debug);
+ }
+
+ DRW_framebuffer_bind(fbl->main);
+}
+
+void EEVEE_effects_do_gtao(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_TextureList *txl = vedata->txl;
+ EEVEE_FramebufferList *fbl = vedata->fbl;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+
+ if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ e_data.depth_src = dtxl->depth;
+
+ DRW_stats_group_start("GTAO Horizon Scan");
+ for (effects->ao_sample_nbr = 0.0;
+ effects->ao_sample_nbr < effects->ao_samples;
+ ++effects->ao_sample_nbr)
+ {
+ DRW_framebuffer_texture_detach(txl->gtao_horizons);
+ DRW_framebuffer_texture_layer_attach(fbl->gtao_fb, txl->gtao_horizons, 0, (int)effects->ao_sample_nbr, 0);
+ DRW_framebuffer_bind(fbl->gtao_fb);
+
+ DRW_draw_pass(psl->ao_horizon_search);
+ }
+ DRW_stats_group_end();
+
+ /* Restore */
+ DRW_framebuffer_bind(fbl->main);
+ }
}
#define SWAP_DOUBLE_BUFFERS() { \
@@ -1285,10 +1441,10 @@ void EEVEE_draw_effects(EEVEE_Data *vedata)
DRW_transform_to_display(effects->source_buffer);
/* Debug : Ouput buffer to view. */
- if ((G.debug_value > 0) && (G.debug_value <= 5)) {
+ if ((G.debug_value > 0) && (G.debug_value <= 6)) {
switch (G.debug_value) {
case 1:
- if (stl->g_data->minzbuffer) DRW_transform_to_display(stl->g_data->minzbuffer);
+ if (txl->maxzbuffer) DRW_transform_to_display(txl->maxzbuffer);
break;
case 2:
if (stl->g_data->ssr_hit_output[0]) DRW_transform_to_display(stl->g_data->ssr_hit_output[0]);
@@ -1302,6 +1458,9 @@ void EEVEE_draw_effects(EEVEE_Data *vedata)
case 5:
if (txl->color_double_buffer) DRW_transform_to_display(txl->color_double_buffer);
break;
+ case 6:
+ if (stl->g_data->gtao_horizons_debug) DRW_transform_to_display(stl->g_data->gtao_horizons_debug);
+ break;
default:
break;
}
@@ -1335,6 +1494,9 @@ void EEVEE_effects_free(void)
}
DRW_SHADER_FREE_SAFE(e_data.downsample_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gtao_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gtao_debug_sh);
+
DRW_SHADER_FREE_SAFE(e_data.volumetric_upsample_sh);
DRW_SHADER_FREE_SAFE(e_data.minz_downlevel_sh);
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 14791952560..4ff36816b7c 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -138,6 +138,7 @@ static void EEVEE_cache_finish(void *vedata)
static void EEVEE_draw_scene(void *vedata)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
+ EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_FramebufferList *fbl = ((EEVEE_Data *)vedata)->fbl;
EEVEE_SceneLayerData *sldata = EEVEE_scene_layer_data_get();
@@ -148,7 +149,18 @@ static void EEVEE_draw_scene(void *vedata)
* when using opengl render. */
int loop_ct = DRW_state_is_image_render() ? 4 : 1;
+ static float rand = 0.0f;
+
+ /* XXX temp for denoising render. TODO plug number of samples here */
+ if (DRW_state_is_image_render()) {
+ rand += 1.0f / 8.0f;
+ rand = rand - floorf(rand);
+ /* Set jitter offset */
+ stl->effects->ao_offset = rand * stl->effects->ao_samples_inv;
+ }
+
while (loop_ct--) {
+
/* Refresh shadows */
DRW_stats_group_start("Shadows");
EEVEE_draw_shadows(sldata, psl);
@@ -176,6 +188,9 @@ static void EEVEE_draw_scene(void *vedata)
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
DRW_stats_group_end();
+ /* Compute GTAO Horizons */
+ EEVEE_effects_do_gtao(sldata, vedata);
+
/* Restore main FB */
DRW_framebuffer_bind(fbl->main);
@@ -271,8 +286,11 @@ static void EEVEE_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDPr
BKE_collection_engine_property_add_bool(props, "gtao_enable", false);
BKE_collection_engine_property_add_bool(props, "gtao_use_bent_normals", true);
+ BKE_collection_engine_property_add_bool(props, "gtao_denoise", true);
+ BKE_collection_engine_property_add_bool(props, "gtao_bounce", true);
BKE_collection_engine_property_add_float(props, "gtao_distance", 0.2f);
BKE_collection_engine_property_add_float(props, "gtao_factor", 1.0f);
+ BKE_collection_engine_property_add_float(props, "gtao_quality", 0.25f);
BKE_collection_engine_property_add_int(props, "gtao_samples", 2);
BKE_collection_engine_property_add_bool(props, "dof_enable", false);
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
index b3774698e4f..f77637edac0 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -969,8 +969,7 @@ static void render_scene_to_probe(
/* Disable AO until we find a way to hide really bad discontinuities between cubefaces. */
tmp_ao_dist = stl->effects->ao_dist;
tmp_ao_samples = stl->effects->ao_samples;
- stl->effects->ao_dist = 0.0f;
- stl->effects->ao_samples = 0.0f;
+ stl->effects->ao_settings = 0.0f; /* Disable AO */
/* 1 - Render to each cubeface individually.
* We do this instead of using geometry shader because a) it's faster,
@@ -1099,6 +1098,9 @@ static void render_scene_to_planar(
EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer);
+ /* Compute GTAO Horizons */
+ EEVEE_effects_do_gtao(sldata, vedata);
+
/* Rebind Planar FB */
DRW_framebuffer_bind(fbl->planarref_fb);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index e8bf461e91f..ef330c424ab 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -285,15 +285,9 @@ static char *eevee_get_defines(int options)
if ((options & VAR_MAT_PROBE) != 0) {
BLI_dynstr_appendf(ds, "#define PROBE_CAPTURE\n");
}
- if ((options & VAR_MAT_AO) != 0) {
- BLI_dynstr_appendf(ds, "#define USE_AO\n");
- }
if ((options & VAR_MAT_FLAT) != 0) {
BLI_dynstr_appendf(ds, "#define USE_FLAT_NORMAL\n");
}
- if ((options & VAR_MAT_BENT) != 0) {
- BLI_dynstr_appendf(ds, "#define USE_BENT_NORMAL\n");
- }
if ((options & VAR_MAT_CLIP) != 0) {
BLI_dynstr_appendf(ds, "#define USE_ALPHA_CLIP\n");
}
@@ -379,6 +373,7 @@ static void add_standard_uniforms(
DRW_shgroup_uniform_buffer(shgrp, "shadowCubes", &sldata->shadow_depth_cube_pool);
DRW_shgroup_uniform_buffer(shgrp, "shadowCascades", &sldata->shadow_depth_cascade_pool);
DRW_shgroup_uniform_int(shgrp, "outputSsrId", ssr_id, 1);
+ DRW_shgroup_uniform_vec4(shgrp, "aoParameters[0]", &vedata->stl->effects->ao_dist, 2);
if (refract_depth != NULL) {
DRW_shgroup_uniform_float(shgrp, "refractionDepth", refract_depth, 1);
}
@@ -395,7 +390,8 @@ static void add_standard_uniforms(
DRW_shgroup_uniform_int(shgrp, "rayCount", &vedata->stl->effects->ssr_ray_count, 1);
}
if (vedata->stl->effects->use_ao) {
- DRW_shgroup_uniform_vec3(shgrp, "aoParameters", &vedata->stl->effects->ao_dist, 1);
+ DRW_shgroup_uniform_buffer(shgrp, "horizonBuffer", &vedata->txl->gtao_horizons);
+ DRW_shgroup_uniform_ivec2(shgrp, "aoHorizonTexSize", (int *)vedata->stl->effects->ao_texsize, 1);
}
}
@@ -614,14 +610,11 @@ struct GPUMaterial *EEVEE_material_world_volume_get(
struct GPUMaterial *EEVEE_material_mesh_get(
struct Scene *scene, Material *ma,
- bool use_ao, bool use_bent_normals, bool use_blend,
- bool use_multiply, bool use_refract)
+ bool use_blend, bool use_multiply, bool use_refract)
{
const void *engine = &DRW_engine_viewport_eevee_type;
int options = VAR_MAT_MESH;
- if (use_ao) options |= VAR_MAT_AO;
- if (use_bent_normals) options |= VAR_MAT_BENT;
if (use_blend) options |= VAR_MAT_BLEND;
if (use_multiply) options |= VAR_MAT_MULT;
if (use_refract) options |= VAR_MAT_REFRACT;
@@ -687,15 +680,11 @@ struct GPUMaterial *EEVEE_material_mesh_depth_get(
}
struct GPUMaterial *EEVEE_material_hair_get(
- struct Scene *scene, Material *ma,
- bool use_ao, bool use_bent_normals)
+ struct Scene *scene, Material *ma)
{
const void *engine = &DRW_engine_viewport_eevee_type;
int options = VAR_MAT_MESH | VAR_MAT_HAIR;
- if (use_ao) options |= VAR_MAT_AO;
- if (use_bent_normals) options |= VAR_MAT_BENT;
-
GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine, options);
if (mat) {
return mat;
@@ -718,15 +707,13 @@ struct GPUMaterial *EEVEE_material_hair_get(
**/
static struct DRWShadingGroup *EEVEE_default_shading_group_create(
EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata, DRWPass *pass,
- bool is_hair, bool is_flat_normal, bool use_ao, bool use_bent_normals, bool use_blend, bool use_ssr)
+ bool is_hair, bool is_flat_normal, bool use_blend, bool use_ssr)
{
static int ssr_id;
ssr_id = (use_ssr) ? 0 : -1;
int options = VAR_MAT_MESH;
if (is_hair) options |= VAR_MAT_HAIR;
- if (use_ao) options |= VAR_MAT_AO;
- if (use_bent_normals) options |= VAR_MAT_BENT;
if (is_flat_normal) options |= VAR_MAT_FLAT;
if (use_blend) options |= VAR_MAT_BLEND;
@@ -745,15 +732,13 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create(
**/
static struct DRWShadingGroup *EEVEE_default_shading_group_get(
EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata,
- bool is_hair, bool is_flat_normal, bool use_ao, bool use_bent_normals, bool use_ssr)
+ bool is_hair, bool is_flat_normal, bool use_ssr)
{
static int ssr_id;
ssr_id = (use_ssr) ? 0 : -1;
int options = VAR_MAT_MESH;
if (is_hair) options |= VAR_MAT_HAIR;
- if (use_ao) options |= VAR_MAT_AO;
- if (use_bent_normals) options |= VAR_MAT_BENT;
if (is_flat_normal) options |= VAR_MAT_FLAT;
if (e_data.default_lit[options] == NULL) {
@@ -933,7 +918,7 @@ static void material_opaque(
/* This will have been created already, just perform a lookup. */
*gpumat = (use_gpumat) ? EEVEE_material_mesh_get(
- scene, ma, stl->effects->use_ao, stl->effects->use_bent_normals, false, false, use_refract) : NULL;
+ scene, ma, false, false, use_refract) : NULL;
*gpumat_depth = (use_gpumat) ? EEVEE_material_mesh_depth_get(
scene, ma, (ma->blend_method == MA_BM_HASHED), false) : NULL;
return;
@@ -941,8 +926,7 @@ static void material_opaque(
if (use_gpumat) {
/* Shading */
- *gpumat = EEVEE_material_mesh_get(scene, ma,
- stl->effects->use_ao, stl->effects->use_bent_normals, false, false, use_refract);
+ *gpumat = EEVEE_material_mesh_get(scene, ma, false, false, use_refract);
*shgrp = DRW_shgroup_material_create(*gpumat, use_refract ? psl->refract_pass : psl->material_pass);
if (*shgrp) {
@@ -986,8 +970,7 @@ static void material_opaque(
/* Fallback to default shader */
if (*shgrp == NULL) {
- *shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, use_flat_nor,
- stl->effects->use_ao, stl->effects->use_bent_normals, stl->effects->use_ssr);
+ *shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, use_flat_nor, stl->effects->use_ssr);
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
@@ -1031,9 +1014,7 @@ static void material_transparent(
if (ma->use_nodes && ma->nodetree) {
/* Shading */
- *gpumat = EEVEE_material_mesh_get(scene, ma,
- stl->effects->use_ao, stl->effects->use_bent_normals,
- true, (ma->blend_method == MA_BM_MULTIPLY), use_refract);
+ *gpumat = EEVEE_material_mesh_get(scene, ma, true, (ma->blend_method == MA_BM_MULTIPLY), use_refract);
*shgrp = DRW_shgroup_material_create(*gpumat, psl->transparent_pass);
if (*shgrp) {
@@ -1054,7 +1035,7 @@ static void material_transparent(
if (*shgrp == NULL) {
*shgrp = EEVEE_default_shading_group_create(
sldata, vedata, psl->transparent_pass,
- false, use_flat_nor, stl->effects->use_ao, stl->effects->use_bent_normals, true, false);
+ false, use_flat_nor, true, false);
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
@@ -1267,8 +1248,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
}
else {
if (ma->use_nodes && ma->nodetree) {
- struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma,
- stl->effects->use_ao, stl->effects->use_bent_normals);
+ struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma);
shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass);
if (shgrp) {
@@ -1290,8 +1270,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
/* Fallback to default shader */
if (shgrp == NULL) {
- shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false,
- stl->effects->use_ao, stl->effects->use_bent_normals, stl->effects->use_ssr);
+ shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false, stl->effects->use_ssr);
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 8afa31a03e0..3fe07e8e653 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -63,14 +63,12 @@ enum {
VAR_MAT_MESH = (1 << 0),
VAR_MAT_PROBE = (1 << 1),
VAR_MAT_HAIR = (1 << 2),
- VAR_MAT_AO = (1 << 3),
- VAR_MAT_FLAT = (1 << 4),
- VAR_MAT_BENT = (1 << 5),
- VAR_MAT_BLEND = (1 << 6),
+ VAR_MAT_FLAT = (1 << 3),
+ VAR_MAT_BLEND = (1 << 4),
/* Max number of variation */
/* IMPORTANT : Leave it last and set
* it's value accordingly. */
- VAR_MAT_MAX = (1 << 7),
+ VAR_MAT_MAX = (1 << 5),
/* These are options that are not counted in VAR_MAT_MAX
* because they are not cumulative with the others above. */
VAR_MAT_CLIP = (1 << 8),
@@ -95,6 +93,8 @@ typedef struct EEVEE_PassList {
struct DRWPass *probe_planar_downsample_ps;
/* Effects */
+ struct DRWPass *ao_horizon_search;
+ struct DRWPass *ao_horizon_debug;
struct DRWPass *motion_blur;
struct DRWPass *bloom_blit;
struct DRWPass *bloom_downsample_first;
@@ -138,6 +138,8 @@ typedef struct EEVEE_PassList {
typedef struct EEVEE_FramebufferList {
/* Effects */
+ struct GPUFrameBuffer *gtao_fb;
+ struct GPUFrameBuffer *gtao_debug_fb;
struct GPUFrameBuffer *downsample_fb;
struct GPUFrameBuffer *effect_fb;
struct GPUFrameBuffer *bloom_blit_fb;
@@ -167,7 +169,6 @@ typedef struct EEVEE_TextureList {
struct GPUTexture *bloom_blit; /* R16_G16_B16 */
struct GPUTexture *bloom_downsample[MAX_BLOOM_STEP]; /* R16_G16_B16 */
struct GPUTexture *bloom_upsample[MAX_BLOOM_STEP-1]; /* R16_G16_B16 */
-
struct GPUTexture *ssr_normal_input;
struct GPUTexture *ssr_specrough_input;
struct GPUTexture *refract_color;
@@ -175,6 +176,8 @@ typedef struct EEVEE_TextureList {
struct GPUTexture *planar_pool;
struct GPUTexture *planar_depth;
+ struct GPUTexture *gtao_horizons;
+
struct GPUTexture *maxzbuffer;
struct GPUTexture *color; /* R16_G16_B16 */
@@ -342,7 +345,10 @@ typedef struct EEVEE_EffectsInfo {
/* Ambient Occlusion */
bool use_ao, use_bent_normals;
- float ao_dist, ao_samples, ao_factor;
+ float ao_dist, ao_samples, ao_factor, ao_samples_inv;
+ float ao_offset, ao_bounce_fac, ao_quality, ao_settings;
+ float ao_sample_nbr;
+ int ao_texsize[2], hori_tex_layers;
/* Motion Blur */
float current_ndc_to_world[4][4];
@@ -382,6 +388,7 @@ enum {
EFFECT_SSR = (1 << 4),
EFFECT_DOUBLE_BUFFER = (1 << 5), /* Not really an effect but a feature */
EFFECT_REFRACT = (1 << 6),
+ EFFECT_GTAO = (1 << 7),
};
/* ************** SCENE LAYER DATA ************** */
@@ -479,6 +486,7 @@ typedef struct EEVEE_PrivateData {
struct GPUTexture *ssr_hit_output[4];
struct GPUTexture *volumetric;
struct GPUTexture *volumetric_transmit;
+ struct GPUTexture *gtao_horizons_debug;
float background_alpha; /* TODO find a better place for this. */
float viewvecs[2][4];
/* For planar probes */
@@ -507,9 +515,9 @@ struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, str
struct GPUMaterial *EEVEE_material_world_volume_get(
struct Scene *scene, struct World *wo, bool use_lights, bool use_volume_shadows, bool is_homogeneous, bool use_color_transmit);
struct GPUMaterial *EEVEE_material_mesh_get(
- struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals, bool use_blend, bool use_multiply, bool use_refract);
+ struct Scene *scene, Material *ma, bool use_blend, bool use_multiply, bool use_refract);
struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow);
-struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals);
+struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma);
void EEVEE_materials_free(void);
void EEVEE_draw_default_passes(EEVEE_PassList *psl);
@@ -542,6 +550,7 @@ void EEVEE_downsample_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_src,
void EEVEE_effects_do_volumetrics(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_effects_do_ssr(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_effects_do_refraction(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata);
+void EEVEE_effects_do_gtao(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_draw_effects(EEVEE_Data *vedata);
void EEVEE_effects_free(void);
diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
index 97f27ea96d6..e346009bba8 100644
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
@@ -4,14 +4,104 @@
* http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx */
#define MAX_PHI_STEP 32
-/* NOTICE : this is multiplied by 2 */
-#define MAX_THETA_STEP 12
+#define MAX_SEARCH_ITER 32
+#define MAX_LOD 6.0
+
+#ifndef UTIL_TEX
+#define UTIL_TEX
+uniform sampler2DArray utilTex;
+#endif /* UTIL_TEX */
+
+uniform vec4 aoParameters[2];
+uniform sampler2DArray horizonBuffer;
+
+/* Cannot use textureSize(horizonBuffer) when rendering to it */
+uniform ivec2 aoHorizonTexSize;
+
+#define aoDistance aoParameters[0].x
+#define aoSamples aoParameters[0].y
+#define aoFactor aoParameters[0].z
+#define aoInvSamples aoParameters[0].w
+
+#define aoOffset aoParameters[1].x
+#define aoBounceFac aoParameters[1].y
+#define aoQuality aoParameters[1].z
+#define aoSettings aoParameters[1].w
-uniform vec3 aoParameters;
+#define USE_AO 1
+#define USE_BENT_NORMAL 2
+#define USE_DENOISE 4
-#define aoDistance aoParameters.x
-#define aoSamples aoParameters.y
-#define aoFactor aoParameters.z
+vec2 pack_horizons(vec2 v) { return v * 0.5 + 0.5; }
+vec2 unpack_horizons(vec2 v) { return v * 2.0 - 1.0; }
+
+/* Returns the texel coordinate in horizonBuffer
+ * for a given fullscreen coord */
+ivec2 get_hr_co(ivec2 fs_co)
+{
+ bvec2 quarter = notEqual(fs_co & ivec2(1), ivec2(0));
+
+ ivec2 hr_co = fs_co / 2;
+ hr_co += ivec2(quarter) * (aoHorizonTexSize / 2);
+
+ return hr_co;
+}
+
+/* Returns the texel coordinate in fullscreen (depthBuffer)
+ * for a given horizonBuffer coord */
+ivec2 get_fs_co(ivec2 hr_co)
+{
+ hr_co *= 2;
+ bvec2 quarter = greaterThanEqual(hr_co, aoHorizonTexSize);
+
+ hr_co -= ivec2(quarter) * (aoHorizonTexSize - 1);
+
+ return hr_co;
+}
+
+/* Returns the phi angle in horizonBuffer
+ * for a given horizonBuffer coord */
+float get_phi(ivec2 hr_co, ivec2 fs_co, float sample)
+{
+ bvec2 quarter = greaterThanEqual(hr_co, aoHorizonTexSize / 2);
+ ivec2 tex_co = ((int(aoSettings) & USE_DENOISE) != 0) ? hr_co - ivec2(quarter) * (aoHorizonTexSize / 2) : fs_co;
+ float blue_noise = texture(utilTex, vec3((vec2(tex_co) + 0.5) / LUT_SIZE, 2.0)).r;
+
+ float phi = sample * aoInvSamples;
+
+ if ((int(aoSettings) & USE_DENOISE) != 0) {
+ /* Interleaved jitter for spatial 2x2 denoising */
+ phi += 0.25 * aoInvSamples * (float(quarter.x) + 2.0 * float(quarter.y));
+ blue_noise *= 0.25;
+ }
+ /* Blue noise is scaled to cover the rest of the range. */
+ phi += aoInvSamples * blue_noise;
+ /* Rotate everything (for multisampling) */
+ phi += aoOffset;
+ phi *= M_PI;
+
+ return phi;
+}
+
+/* Returns direction jittered offset for a given fullscreen coord */
+float get_offset(ivec2 fs_co, float sample)
+{
+ float offset = sample * aoInvSamples;
+
+ /* Interleaved jitter for spatial 2x2 denoising */
+ offset += 0.25 * dot(vec2(1.0), vec2(fs_co & 1));
+ offset += texture(utilTex, vec3((vec2(fs_co / 2) + 0.5 + 16.0) / LUT_SIZE, 2.0)).r;
+ offset = fract(offset + aoOffset);
+ return offset;
+}
+
+/* Returns maximum screen distance an AO ray can travel for a given view depth */
+vec2 get_max_dir(float view_depth)
+{
+ float homcco = ProjectionMatrix[2][3] * view_depth + ProjectionMatrix[3][3];
+ float max_dist = aoDistance / homcco;
+ return vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * max_dist;
+}
void get_max_horizon_grouped(vec4 co1, vec4 co2, vec3 x, float lod, inout float h)
{
@@ -54,11 +144,10 @@ void get_max_horizon_grouped(vec4 co1, vec4 co2, vec3 x, float lod, inout float
h = mix(h, max(h, s_h.w), blend.w);
}
-#define MAX_ITER 16
-#define MAX_LOD 6.0
-#define QUALITY 0.75
-vec2 search_horizon_sweep(vec2 t_phi, vec3 pos, vec2 uvs, float jitter, vec2 max_dir)
+vec2 search_horizon_sweep(float phi, vec3 pos, vec2 uvs, float jitter, vec2 max_dir)
{
+ vec2 t_phi = vec2(cos(phi), sin(phi)); /* Screen space direction */
+
max_dir *= max_v2(abs(t_phi));
/* Convert to pixel space. */
@@ -84,128 +173,141 @@ vec2 search_horizon_sweep(vec2 t_phi, vec3 pos, vec2 uvs, float jitter, vec2 max
/* This is freaking sexy optimized. */
for (float i = 0.0, ofs = 4.0, time = -1.0;
- i < MAX_ITER && time > times.x;
- i++, time -= ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs))
+ i < MAX_SEARCH_ITER && time > times.x;
+ i++, time -= ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs * aoQuality))
{
vec4 t = max(times.xxxx, vec4(time) - (vec4(0.25, 0.5, 0.75, 1.0) - jitter) * ofs);
vec4 cos1 = uvs.xyxy + t_phi.xyxy * t.xxyy;
vec4 cos2 = uvs.xyxy + t_phi.xyxy * t.zzww;
- get_max_horizon_grouped(cos1, cos2, pos, min(MAX_LOD, i * QUALITY), h.y);
+ float lod = min(MAX_LOD, max(i - jitter * 4.0, 0.0) * aoQuality);
+ get_max_horizon_grouped(cos1, cos2, pos, lod, h.y);
}
for (float i = 0.0, ofs = 4.0, time = 1.0;
- i < MAX_ITER && time < times.y;
- i++, time += ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs))
+ i < MAX_SEARCH_ITER && time < times.y;
+ i++, time += ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs * aoQuality))
{
vec4 t = min(times.yyyy, vec4(time) + (vec4(0.25, 0.5, 0.75, 1.0) - jitter) * ofs);
vec4 cos1 = uvs.xyxy + t_phi.xyxy * t.xxyy;
vec4 cos2 = uvs.xyxy + t_phi.xyxy * t.zzww;
- get_max_horizon_grouped(cos1, cos2, pos, min(MAX_LOD, i * QUALITY), h.x);
+ float lod = min(MAX_LOD, max(i - jitter * 4.0, 0.0) * aoQuality);
+ get_max_horizon_grouped(cos1, cos2, pos, lod, h.x);
}
return h;
}
-void integrate_slice(
- float iter, vec3 x, vec3 normal, vec2 x_, vec2 noise,
- vec2 max_dir, vec2 pixel_ratio, float pixel_len,
- inout float visibility, inout vec3 bent_normal)
+void integrate_slice(vec3 normal, float phi, vec2 horizons, inout float visibility, inout vec3 bent_normal)
{
- float phi = M_PI * ((noise.r + iter) / aoSamples);
-
- /* Rotate with random direction to get jittered result. */
+ /* TODO OPTI Could be precomputed. */
vec2 t_phi = vec2(cos(phi), sin(phi)); /* Screen space direction */
- /* Search maximum horizon angles h1 and h2 */
- vec2 horiz = search_horizon_sweep(t_phi, x, x_, noise.g, max_dir);
-
- /* (Slide 54) */
- float h1 = -fast_acos(horiz.x);
- float h2 = fast_acos(horiz.y);
-
/* Projecting Normal to Plane P defined by t_phi and omega_o */
- vec3 h = vec3(t_phi.y, -t_phi.x, 0.0); /* Normal vector to Integration plane */
+ vec3 np = vec3(t_phi.y, -t_phi.x, 0.0); /* Normal vector to Integration plane */
vec3 t = vec3(-t_phi, 0.0);
- vec3 n_proj = normal - h * dot(h, normal);
+ vec3 n_proj = normal - np * dot(np, normal);
float n_proj_len = max(1e-16, length(n_proj));
- /* Clamping thetas (slide 58) */
float cos_n = clamp(n_proj.z / n_proj_len, -1.0, 1.0);
float n = sign(dot(n_proj, t)) * fast_acos(cos_n); /* Angle between view vec and normal */
- h1 = n + max(h1 - n, -M_PI_2);
- h2 = n + min(h2 - n, M_PI_2);
+
+ /* (Slide 54) */
+ vec2 h = fast_acos(horizons);
+ h.x = -h.x;
+
+ /* Clamping thetas (slide 58) */
+ h.x = n + max(h.x - n, -M_PI_2);
+ h.y = n + min(h.y - n, M_PI_2);
/* Solving inner integral */
- float sin_n = sin(n);
- float h1_2 = 2.0 * h1;
- float h2_2 = 2.0 * h2;
- float vd = (-cos(h1_2 - n) + cos_n + h1_2 * sin_n) + (-cos(h2_2 - n) + cos_n + h2_2 * sin_n);
- vd *= 0.25 * n_proj_len;
- visibility += vd;
-
-#ifdef USE_BENT_NORMAL
+ vec2 h_2 = 2.0 * h;
+ vec2 vd = -cos(h_2 - n) + cos_n + h_2 * sin(n);
+ float vis = (vd.x + vd.y) * 0.25 * n_proj_len;
+
+ visibility += vis;
+
/* Finding Bent normal */
- float b_angle = (h1 + h2) / 2.0;
+ float b_angle = (h.x + h.y) * 0.5;
/* The 0.5 factor below is here to equilibrate the accumulated vectors.
* (sin(b_angle) * -t_phi) will accumulate to (phi_step * result_nor.xy * 0.5).
* (cos(b_angle) * 0.5) will accumulate to (phi_step * result_nor.z * 0.5). */
- /* Weight sample by vd */
- bent_normal += vec3(sin(b_angle) * -t_phi, cos(b_angle) * 0.5) * vd;
-#endif
+ bent_normal += vec3(sin(b_angle) * -t_phi, cos(b_angle) * 0.5);
}
-void gtao(vec3 normal, vec3 position, vec2 noise, out float visibility
-#ifdef USE_BENT_NORMAL
- , out vec3 bent_normal
-#endif
- )
+void denoise_ao(vec3 normal, float frag_depth, inout float visibility, inout vec3 bent_normal)
{
- vec2 screenres = vec2(textureSize(maxzBuffer, 0)) * 2.0;
- vec2 pixel_size = vec2(1.0) / screenres.xy;
+ vec2 d_sign = vec2(ivec2(gl_FragCoord.xy) & 1) - 0.5;
- /* Renaming */
- vec2 x_ = gl_FragCoord.xy * pixel_size; /* x^ Screen coordinate */
- vec3 x = position; /* x view space coordinate */
+ if ((int(aoSettings) & USE_DENOISE) == 0) {
+ d_sign *= 0.0;
+ }
- /* NOTE : We set up integration domain around the camera forward axis
- * and not the view vector like in the paper.
- * This allows us to save a lot of dot products. */
- /* omega_o = vec3(0.0, 0.0, 1.0); */
+ /* 2x2 Bilateral Filter using derivatives. */
+ vec2 n_step = step(-0.2, -abs(vec2(length(dFdx(normal)), length(dFdy(normal)))));
+ vec2 z_step = step(-0.1, -abs(vec2(dFdx(frag_depth), dFdy(frag_depth))));
+
+ visibility -= dFdx(visibility) * d_sign.x * z_step.x * n_step.x;
+ visibility -= dFdy(visibility) * d_sign.y * z_step.y * n_step.y;
+
+ bent_normal -= dFdx(bent_normal) * d_sign.x * z_step.x * n_step.x;
+ bent_normal -= dFdy(bent_normal) * d_sign.y * z_step.y * n_step.y;
+}
+
+void gtao_deferred(vec3 normal, vec3 position, float frag_depth, out float visibility, out vec3 bent_normal)
+{
+ vec2 uvs = get_uvs_from_view(position);
+
+ vec4 texel_size = vec4(-1.0, -1.0, 1.0, 1.0) / vec2(textureSize(depthBuffer, 0)).xyxy;
+
+ ivec2 fs_co = ivec2(gl_FragCoord.xy);
+ ivec2 hr_co = get_hr_co(fs_co);
+
+ bent_normal = vec3(0.0);
+ visibility = 0.0;
+
+ for (float i = 0.0; i < MAX_PHI_STEP; i++) {
+ if (i >= aoSamples) break;
+
+ vec2 horiz = unpack_horizons(texelFetch(horizonBuffer, ivec3(hr_co, int(i)), 0).rg);
+ float phi = get_phi(hr_co, fs_co, i);
+
+ integrate_slice(normal, phi, horiz.xy, visibility, bent_normal);
+ }
+
+ visibility *= aoInvSamples;
+ bent_normal = normalize(bent_normal);
+}
+
+void gtao(vec3 normal, vec3 position, vec2 noise, out float visibility, out vec3 bent_normal)
+{
+ vec2 uvs = get_uvs_from_view(position);
- vec2 pixel_ratio = vec2(screenres.y / screenres.x, 1.0);
- float pixel_len = length(pixel_size);
float homcco = ProjectionMatrix[2][3] * position.z + ProjectionMatrix[3][3];
float max_dist = aoDistance / homcco; /* Search distance */
vec2 max_dir = max_dist * vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]);
- /* Integral over PI */
- visibility = 0.0;
-#ifdef USE_BENT_NORMAL
bent_normal = vec3(0.0);
-#else
- vec3 bent_normal = vec3(0.0);
-#endif
+ visibility = 0.0;
+
for (float i = 0.0; i < MAX_PHI_STEP; i++) {
if (i >= aoSamples) break;
- integrate_slice(i, x, normal, x_, noise, max_dir, pixel_ratio, pixel_len, visibility, bent_normal);
- }
- /* aoSamples can be 0.0 to temporary disable the effect. */
- visibility = clamp(max(1e-8, visibility) / max(1e-8, aoSamples), 1e-8, 1.0);
+ float phi = M_PI * (i + noise.x) * aoInvSamples;
+ vec2 horizons = search_horizon_sweep(phi, position, uvs, noise.g, max_dir);
-#ifdef USE_BENT_NORMAL
- /* The bent normal will show the facet look of the mesh. Try to minimize this. */
- bent_normal = normalize(mix(bent_normal / visibility, normal, visibility * visibility * visibility));
-#endif
+ integrate_slice(normal, phi, horizons, visibility, bent_normal);
+ }
- /* Scale by user factor */
- visibility = pow(visibility, aoFactor);
+ visibility *= aoInvSamples;
+ bent_normal = normalize(bent_normal);
}
/* Multibounce approximation base on surface albedo.
* Page 78 in the .pdf version. */
float gtao_multibounce(float visibility, vec3 albedo)
{
+ if (aoBounceFac == 0.0) return visibility;
+
/* Median luminance. Because Colored multibounce looks bad. */
float lum = dot(albedo, vec3(0.3333));
@@ -220,24 +322,38 @@ float gtao_multibounce(float visibility, vec3 albedo)
/* Use the right occlusion */
float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec2 randuv, out vec3 bent_normal)
{
-#ifdef USE_AO /* Screen Space Occlusion */
-
- float computed_occlusion;
- vec3 vnor = mat3(ViewMatrix) * N;
+ if ((int(aoSettings) & USE_AO) == 0) {
+ bent_normal = N;
+ return user_occlusion;
+ }
+ else {
+ float visibility;
+ vec3 vnor = mat3(ViewMatrix) * N;
-#ifdef USE_BENT_NORMAL
- gtao(vnor, vpos, randuv, computed_occlusion, bent_normal);
- bent_normal = mat3(ViewMatrixInverse) * bent_normal;
+#if defined(MESH_SHADER) && !defined(USE_ALPHA_HASH) && !defined(USE_ALPHA_CLIP) && !defined(SHADOW_SHADER) && !defined(USE_MULTIPLY) && !defined(USE_ALPHA_BLEND)
+ gtao_deferred(vnor, vpos, gl_FragCoord.z, visibility, bent_normal);
#else
- gtao(vnor, vpos, randuv, computed_occlusion);
- bent_normal = N;
+ gtao(vnor, vpos, randuv, visibility, bent_normal);
#endif
- return min(computed_occlusion, user_occlusion);
+ denoise_ao(vnor, gl_FragCoord.z, visibility, bent_normal);
-#else /* No added Occlusion. */
+ /* Prevent some problems down the road. */
+ visibility = max(1e-3, visibility);
- bent_normal = N;
- return user_occlusion;
+ if ((int(aoSettings) & USE_BENT_NORMAL) != 0) {
+ /* The bent normal will show the facet look of the mesh. Try to minimize this. */
+ float mix_fac = visibility * visibility;
+ bent_normal = normalize(mix(bent_normal, vnor, mix_fac));
-#endif
+ bent_normal = transform_direction(ViewMatrixInverse, bent_normal);
+ }
+ else {
+ bent_normal = N;
+ }
+
+ /* Scale by user factor */
+ visibility = pow(visibility, aoFactor);
+
+ return min(visibility, user_occlusion);
+ }
}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
new file mode 100644
index 00000000000..1c63051c65b
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
@@ -0,0 +1,69 @@
+/**
+ * This shader only compute maximum horizon angles for each directions.
+ * The final integration is done at the resolve stage with the shading normal.
+ **/
+
+uniform float rotationOffset;
+
+out vec4 FragColor;
+
+#ifdef DEBUG_AO
+uniform sampler2D normalBuffer;
+
+void main()
+{
+ vec4 texel_size = 1.0 / vec2(textureSize(depthBuffer, 0)).xyxy;
+ vec2 uvs = saturate(gl_FragCoord.xy * texel_size.xy);
+
+ float depth = textureLod(depthBuffer, uvs, 0.0).r;
+
+ vec3 viewPosition = get_view_space_from_depth(uvs, depth);
+ vec3 V = viewCameraVec;
+ vec3 normal = normal_decode(texture(normalBuffer, uvs).rg, V);
+
+ vec3 bent_normal;
+ float visibility;
+#if 1
+ gtao_deferred(normal, viewPosition, depth, visibility, bent_normal);
+#else
+ vec2 rand = vec2((1.0 / 4.0) * float((int(gl_FragCoord.y) & 0x1) * 2 + (int(gl_FragCoord.x) & 0x1)), 0.5);
+ rand = fract(rand.x + texture(utilTex, vec3(floor(gl_FragCoord.xy * 0.5) / LUT_SIZE, 2.0)).rg);
+ gtao(normal, viewPosition, rand, visibility, bent_normal);
+#endif
+ denoise_ao(normal, depth, visibility, bent_normal);
+
+ FragColor = vec4(visibility);
+}
+
+#else
+uniform float sampleNbr;
+
+void main()
+{
+ ivec2 hr_co = ivec2(gl_FragCoord.xy);
+ ivec2 fs_co = get_fs_co(hr_co);
+
+ vec2 uvs = saturate((vec2(fs_co) + 0.5) / vec2(textureSize(depthBuffer, 0)));
+ float depth = textureLod(depthBuffer, uvs, 0.0).r;
+
+ if (depth == 1.0) {
+ /* Do not trace for background */
+ FragColor = vec4(0.0);
+ return;
+ }
+
+ /* Avoid self shadowing. */
+ depth = saturate(depth - 3e-6); /* Tweaked for 24bit depth buffer. */
+
+ vec3 viewPosition = get_view_space_from_depth(uvs, depth);
+
+ float phi = get_phi(hr_co, fs_co, sampleNbr);
+ float offset = get_offset(fs_co, sampleNbr);
+ vec2 max_dir = get_max_dir(viewPosition.z);
+
+ FragColor.xy = search_horizon_sweep(phi, viewPosition, uvs, offset, max_dir);
+
+ /* Resize output for integer texture. */
+ FragColor = pack_horizons(FragColor.xy).xyxy;
+}
+#endif
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 23f858f129b..acccb9593bf 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -2617,7 +2617,10 @@ RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(hair_brightness_randomness)
/* SceneLayer settings. */
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(gtao_enable)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(gtao_use_bent_normals)
+RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(gtao_denoise)
+RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(gtao_bounce)
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(gtao_factor)
+RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(gtao_quality)
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(gtao_distance)
RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(gtao_samples)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(dof_enable)
@@ -6368,14 +6371,36 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
+ prop = RNA_def_property(srna, "gtao_denoise", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_gtao_denoise_get",
+ "rna_LayerEngineSettings_Eevee_gtao_denoise_set");
+ RNA_def_property_ui_text(prop, "Denoise", "Use denoising to filter the resulting occlusion and bent normal but exhibit 2x2 pixel blocks");
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+ RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
+
+ prop = RNA_def_property(srna, "gtao_bounce", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_gtao_bounce_get",
+ "rna_LayerEngineSettings_Eevee_gtao_bounce_set");
+ RNA_def_property_ui_text(prop, "Bounces Approximation", "An approximation to simulate light bounces "
+ "giving less occlusion on brighter objects");
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+ RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
+
prop = RNA_def_property(srna, "gtao_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Eevee_gtao_factor_get", "rna_LayerEngineSettings_Eevee_gtao_factor_set", NULL);
RNA_def_property_ui_text(prop, "Factor", "Factor for ambient occlusion blending");
- RNA_def_property_range(prop, 0, FLT_MAX);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2);
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update");
+ prop = RNA_def_property(srna, "gtao_quality", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Eevee_gtao_quality_get", "rna_LayerEngineSettings_Eevee_gtao_quality_set", NULL);
+ RNA_def_property_ui_text(prop, "Trace Quality", "Quality of the horizon search");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+ RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update");
+
prop = RNA_def_property(srna, "gtao_distance", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Eevee_gtao_distance_get", "rna_LayerEngineSettings_Eevee_gtao_distance_set", NULL);
RNA_def_property_ui_text(prop, "Distance", "Distance of object that contribute to the ambient occlusion effect");