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}; @@ -866,6 +952,33 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_call_add(grp, quad, NULL); } + { + 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); @@ -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 -uniform vec3 aoParameters; +#ifndef UTIL_TEX +#define UTIL_TEX +uniform sampler2DArray utilTex; +#endif /* UTIL_TEX */ -#define aoDistance aoParameters.x -#define aoSamples aoParameters.y -#define aoFactor aoParameters.z +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 + +#define USE_AO 1 +#define USE_BENT_NORMAL 2 +#define USE_DENOISE 4 + +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; + 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; -#ifdef USE_BENT_NORMAL /* 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); + + float phi = M_PI * (i + noise.x) * aoInvSamples; + vec2 horizons = search_horizon_sweep(phi, position, uvs, noise.g, max_dir); + + integrate_slice(normal, phi, horizons, 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); - -#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 - - /* 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 */ + if ((int(aoSettings) & USE_AO) == 0) { + bent_normal = N; + return user_occlusion; + } + else { + float visibility; + vec3 vnor = mat3(ViewMatrix) * N; - float computed_occlusion; - 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");