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 /source/blender/draw
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).
Diffstat (limited to 'source/blender/draw')
-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
8 files changed, 514 insertions, 158 deletions
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