diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 3abaa490d02..36e0f7513d5 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -330,7 +330,7 @@ class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel):
 
 
 class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
-    bl_label = "Screen Space Reflections"
+    bl_label = "Screen Space Raytracing"
     bl_options = {'DEFAULT_CLOSED'}
     COMPAT_ENGINES = {'BLENDER_EEVEE'}
 
@@ -354,12 +354,26 @@ class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
         col.active = props.use_ssr
         col.prop(props, "use_ssr_refraction", text="Refraction")
         col.prop(props, "use_ssr_halfres")
+        col.prop(props, "ssr_border_fade")
+
+        col.label(text="Specular:", text_ctxt="", translate=False, icon='NONE', icon_value=0)
+
         col.prop(props, "ssr_quality")
         col.prop(props, "ssr_max_roughness")
         col.prop(props, "ssr_thickness")
-        col.prop(props, "ssr_border_fade")
         col.prop(props, "ssr_firefly_fac")
 
+        col.label(text="Diffuse:", text_ctxt="", translate=False, icon='NONE', icon_value=0)
+
+        #col.prop(props, "ssr_diffuse_enable")
+        col.prop(props, "ssr_diffuse_intensity")
+        col.prop(props, "ssr_diffuse_quality")
+        col.prop(props, "ssr_diffuse_thickness")
+        #col.prop(props, "ssr_diffuse_trace_bias")
+        col.prop(props, "ssr_diffuse_resolve_bias")
+        col.prop(props, "ssr_diffuse_clamp")
+        col.prop(props, "ssr_diffuse_ao")
+
 
 class RENDER_PT_eevee_shadows(RenderButtonsPanel, Panel):
     bl_label = "Shadows"
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index ef2e196094e..018a503c21b 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -2249,6 +2249,14 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
         scene->eevee.ssr_thickness = 0.2f;
         scene->eevee.ssr_border_fade = 0.075f;
         scene->eevee.ssr_firefly_fac = 10.0f;
+        scene->eevee.ssr_diffuse_intensity = 1.0f;
+        scene->eevee.ssr_diffuse_thickness = 1.0f;
+        scene->eevee.ssr_diffuse_trace_bias = 0.0f;
+        scene->eevee.ssr_diffuse_resolve_bias = 0.1f;
+        scene->eevee.ssr_diffuse_enable = 1.0f;
+        scene->eevee.ssr_diffuse_quality = 0.25f;
+        scene->eevee.ssr_diffuse_clamp = 1.0f;
+        scene->eevee.ssr_diffuse_ao = 1.0f;
 
         scene->eevee.volumetric_start = 0.1f;
         scene->eevee.volumetric_end = 100.0f;
@@ -2369,6 +2377,14 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
         EEVEE_GET_FLOAT(props, ssr_thickness);
         EEVEE_GET_FLOAT(props, ssr_border_fade);
         EEVEE_GET_FLOAT(props, ssr_firefly_fac);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_intensity);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_thickness);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_trace_bias);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_resolve_bias);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_enable);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_quality);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_clamp);
+        EEVEE_GET_FLOAT(props, ssr_diffuse_ao);
 
         EEVEE_GET_FLOAT(props, volumetric_start);
         EEVEE_GET_FLOAT(props, volumetric_end);
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 5c8e9da41c4..be22087ca09 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1758,6 +1758,18 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
     }
     FOREACH_NODETREE_END;
   }
+  if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "diffuse_intensity")) {
+    LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+        scene->eevee.ssr_diffuse_intensity = 1.0f;
+        scene->eevee.ssr_diffuse_thickness = 1.0f;
+        scene->eevee.ssr_diffuse_trace_bias = 0.0f;
+        scene->eevee.ssr_diffuse_resolve_bias = 0.1f;
+        scene->eevee.ssr_diffuse_enable = 1.0f;
+        scene->eevee.ssr_diffuse_quality = 0.25f;
+        scene->eevee.ssr_diffuse_clamp = 1.0f;
+        scene->eevee.ssr_diffuse_ao = 1.0f;
+    }
+  }
 
   if (!MAIN_VERSION_ATLEAST(bmain, 293, 9)) {
     if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "bokeh_overblur")) {
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index dff9e302fdf..78dad716d7f 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -320,6 +320,7 @@ data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC)
 data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC)
 data_to_c_simple(intern/shaders/common_math_lib.glsl SRC)
 data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC)
+data_to_c_simple(intern/shaders/common_colorpacking_lib.glsl SRC)
 data_to_c_simple(intern/shaders/common_view_lib.glsl SRC)
 data_to_c_simple(intern/shaders/common_fxaa_lib.glsl SRC)
 data_to_c_simple(intern/shaders/common_smaa_lib.glsl SRC)
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 4e32854dedc..2fba3ec799f 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -741,6 +741,8 @@ typedef struct EEVEE_EffectsInfo {
   struct GPUTexture *ssr_specrough_input;
   struct GPUTexture *ssr_hit_output;
   struct GPUTexture *ssr_pdf_output;
+  struct GPUTexture *ssr_hit_outputGI; //MINE
+  struct GPUTexture *ssr_pdf_outputGI; //MINE
   /* Temporal Anti Aliasing */
   int taa_reproject_sample;
   int taa_current_sample;
@@ -908,6 +910,15 @@ typedef struct EEVEE_CommonUniformBuffer {
   float alpha_hash_scale;  /* float */
   float pad7;              /* float */
   float pad8;              /* float */
+  /* Custom MINE GI */
+  float ssr_diffuse_intensity;                        /* float */
+  float ssr_diffuse_thickness;                        /* float */
+  float ssr_diffuse_trace_bias;                       /* float */ //Not in use
+  float ssr_diffuse_resolve_bias;                     /* float */
+  float ssr_diffuse_enable;                           /* bool */ //Not in use
+  float ssr_diffuse_quality;                          /* float */
+  float ssr_diffuse_clamp;                            /* float */
+  float ssr_diffuse_ao;                               /* float */
 } EEVEE_CommonUniformBuffer;
 
 BLI_STATIC_ASSERT_ALIGN(EEVEE_CommonUniformBuffer, 16)
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
index f9e22f5c08d..1636d32915a 100644
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
@@ -86,23 +86,38 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
     common_data->ssr_max_roughness = scene_eval->eevee.ssr_max_roughness;
     common_data->ssr_quality = 1.0f - 0.95f * scene_eval->eevee.ssr_quality;
     common_data->ssr_brdf_bias = 0.1f + common_data->ssr_quality * 0.6f; /* Range [0.1, 0.7]. */
+    common_data->ssr_diffuse_intensity = scene_eval->eevee.ssr_diffuse_intensity;
+    common_data->ssr_diffuse_thickness = scene_eval->eevee.ssr_diffuse_thickness;
+    //common_data->ssr_diffuse_trace_bias = scene_eval->eevee.ssr_diffuse_trace_bias;
+    common_data->ssr_diffuse_trace_bias = 0.0f;
+    common_data->ssr_diffuse_resolve_bias = scene_eval->eevee.ssr_diffuse_resolve_bias;
+    //common_data->ssr_diffuse_enable = scene_eval->eevee.ssr_diffuse_enable;
+    common_data->ssr_diffuse_enable = 1.0f;
+    common_data->ssr_diffuse_quality = scene_eval->eevee.ssr_diffuse_quality;
+    common_data->ssr_diffuse_clamp = scene_eval->eevee.ssr_diffuse_clamp;
+    common_data->ssr_diffuse_ao = scene_eval->eevee.ssr_diffuse_ao;
 
     if (common_data->ssr_firefly_fac < 1e-8f) {
       common_data->ssr_firefly_fac = FLT_MAX;
     }
+    if (common_data->ssr_diffuse_clamp < 1e-8f) {
+      common_data->ssr_diffuse_clamp = FLT_MAX;
+    }
 
     const int divisor = (effects->reflection_trace_full) ? 1 : 2;
     int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor};
     const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
     const bool high_qual_input = true; /* TODO dither low quality input */
     const eGPUTextureFormat format = (high_qual_input) ? GPU_RGBA16F : GPU_RGBA8;
+    const eGPUTextureFormat format32bit = GPU_RGBA32F;
+
 
     tracing_res[0] = max_ii(1, tracing_res[0]);
     tracing_res[1] = max_ii(1, tracing_res[1]);
 
     /* MRT for the shading pass in order to output needed data for the SSR pass. */
     effects->ssr_specrough_input = DRW_texture_pool_query_2d(
-        size_fs[0], size_fs[1], format, &draw_engine_eevee_type);
+        size_fs[0], size_fs[1], format32bit, &draw_engine_eevee_type);
 
     GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_specrough_input, 2, 0);
 
@@ -111,11 +126,17 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
         tracing_res[0], tracing_res[1], GPU_RG16I, &draw_engine_eevee_type);
     effects->ssr_pdf_output = DRW_texture_pool_query_2d(
         tracing_res[0], tracing_res[1], GPU_R16F, &draw_engine_eevee_type);
+    effects->ssr_hit_outputGI = DRW_texture_pool_query_2d(
+        tracing_res[0], tracing_res[1], GPU_RG16I, &draw_engine_eevee_type);
+    effects->ssr_pdf_outputGI = DRW_texture_pool_query_2d(
+        tracing_res[0], tracing_res[1], GPU_R16F, &draw_engine_eevee_type);
 
     GPU_framebuffer_ensure_config(&fbl->screen_tracing_fb,
                                   {GPU_ATTACHMENT_NONE,
                                    GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output),
-                                   GPU_ATTACHMENT_TEXTURE(effects->ssr_pdf_output)});
+                                   GPU_ATTACHMENT_TEXTURE(effects->ssr_pdf_output),
+                                   GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_outputGI),
+                                   GPU_ATTACHMENT_TEXTURE(effects->ssr_pdf_outputGI)});
 
     /* Enable double buffering to be able to read previous frame color */
     return EFFECT_SSR | EFFECT_NORMAL_BUFFER | EFFECT_DOUBLE_BUFFER |
@@ -127,6 +148,8 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
   effects->ssr_specrough_input = NULL;
   effects->ssr_hit_output = NULL;
   effects->ssr_pdf_output = NULL;
+  effects->ssr_hit_outputGI = NULL;
+  effects->ssr_pdf_outputGI = NULL;
 
   return 0;
 }
@@ -189,6 +212,8 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
     DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
     DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output);
     DRW_shgroup_uniform_texture_ref(grp, "pdfBuffer", &effects->ssr_pdf_output);
+    DRW_shgroup_uniform_texture_ref(grp, "hitBufferGI", &effects->ssr_hit_outputGI); //MINE
+    DRW_shgroup_uniform_texture_ref(grp, "pdfBufferGI", &effects->ssr_pdf_outputGI); //MINE
     DRW_shgroup_uniform_texture_ref(grp, "prevColorBuffer", &txl->color_double_buffer);
     DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
     DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index a3e9236dc79..a0b792c2312 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -183,6 +183,7 @@ extern char datatoc_common_math_lib_glsl[];
 extern char datatoc_common_math_geom_lib_glsl[];
 extern char datatoc_common_view_lib_glsl[];
 extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
+extern char datatoc_common_colorpacking_lib_glsl[];
 
 extern char datatoc_ambient_occlusion_lib_glsl[];
 extern char datatoc_background_vert_glsl[];
@@ -281,6 +282,7 @@ static void eevee_shader_library_ensure(void)
     /* NOTE: These need to be ordered by dependencies. */
     DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib);
     DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib);
+    DRW_SHADER_LIB_ADD(e_data.lib, common_colorpacking_lib);
     DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib);
     DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
     DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib);
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
index c5996f5160a..65a166bec94 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
@@ -71,7 +71,11 @@ void closure_Diffuse_indirect_end(ClosureInputDiffuse cl_in,
     cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
   }
   /* Apply occlusion on radiance before the light loop. */
-  cl_out.radiance *= cl_eval.ambient_occlusion;
+  float ao_transfer = 1.0 * cl_eval.ambient_occlusion;
+
+  //cl_out.radiance *= cl_eval.ambient_occlusion;
+  cl_out.radiance *= ao_transfer;
+  cl_out.AO = ao_transfer;
 }
 
 void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in,
@@ -84,4 +88,4 @@ void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in,
   cl_out.radiance = vec3(0.0);
   return;
 #endif
-}
+}
\ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
index 93492762bbe..fe6983ed52e 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
@@ -138,7 +138,7 @@
 #define ClosureInputDummy ClosureOutput
 #define ClosureOutputDummy ClosureOutput
 #define ClosureEvalDummy ClosureOutput
-#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
+#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0), float(0.0))
 #define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY
 #define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY
 #define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
@@ -194,6 +194,7 @@ struct ClosureEvalCommon {
 /* Common cl_out struct used by most closures. */
 struct ClosureOutput {
   vec3 radiance;
+  float AO;
 };
 
 /* Workaround for screenspace shadows in SSR pass. */
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
index 66c467af29b..a7bc85a21f9 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
@@ -69,3 +69,5 @@ void closure_Translucent_eval_end(ClosureInputTranslucent cl_in,
   return;
 #endif
 }
+
+/* TODO ssr_data */
diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
index 38ae3972aa7..707baacf742 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
@@ -1,6 +1,7 @@
 
 #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
 #pragma BLENDER_REQUIRE(renderpass_lib.glsl)
+#pragma BLENDER_REQUIRE(common_colorpacking_lib.glsl)
 
 #ifndef VOLUMETRICS
 
@@ -97,10 +98,18 @@ Closure closure_mix(Closure cl1, Closure cl2, float fac)
   cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac);
   cl.radiance = mix(cl1.radiance, cl2.radiance, fac);
   cl.flag = cl1.flag | cl2.flag;
-  cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac);
+  /* unpack TODO - clean up */
+  vec4 spec_cl1 = vec4(0.0,0.0,0.0,1.0);
+  vec4 spec_cl2 = vec4(0.0,0.0,0.0,1.0);
+  vec4 diffuse_cl1 = vec4(0.0,0.0,0.0,1.0);
+  vec4 diffuse_cl2 = vec4(0.0,0.0,0.0,1.0);
+  unpackVec4(cl1.ssr_data, spec_cl1, diffuse_cl1);
+  unpackVec4(cl2.ssr_data, spec_cl2, diffuse_cl2);
+
+  cl.ssr_data.rgb = packVec3(mix(spec_cl1.rgb, spec_cl2.rgb, fac), mix(diffuse_cl1.rgb, diffuse_cl2.rgb, fac));
   bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
   /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz).*/
-  cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
+  cl.ssr_data.w = (use_cl1_ssr) ? packFloat(spec_cl1.a,  mix(diffuse_cl1.a, diffuse_cl2.a, fac)) : packFloat(spec_cl2.a,  mix(diffuse_cl1.a, diffuse_cl2.a, fac));
   cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
 
 #  ifdef USE_SSS
@@ -120,10 +129,18 @@ Closure closure_add(Closure cl1, Closure cl2)
   cl.radiance = cl1.radiance + cl2.radiance;
   cl.holdout = cl1.holdout + cl2.holdout;
   cl.flag = cl1.flag | cl2.flag;
-  cl.ssr_data = cl1.ssr_data + cl2.ssr_data;
+  /* unpack TODO - clean up */
+  vec4 spec_cl1 = vec4(0.0,0.0,0.0,1.0);
+  vec4 spec_cl2 = vec4(0.0,0.0,0.0,1.0);
+  vec4 diffuse_cl1 = vec4(0.0,0.0,0.0,1.0);
+  vec4 diffuse_cl2 = vec4(0.0,0.0,0.0,1.0);
+  unpackVec4(cl1.ssr_data, spec_cl1, diffuse_cl1);
+  unpackVec4(cl2.ssr_data, spec_cl2, diffuse_cl2);
+
+  cl.ssr_data.rgb = packVec3((spec_cl1.rgb + spec_cl2.rgb), (diffuse_cl1.rgb + diffuse_cl2.rgb));
   bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
   /* When mixing SSR don't blend roughness and normals.*/
-  cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
+  cl.ssr_data.w = (use_cl1_ssr) ? packFloat(spec_cl1.a, diffuse_cl1.a + diffuse_cl2.a): packFloat(spec_cl2.a, diffuse_cl1.a + diffuse_cl2.a);
   cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
 
 #  ifdef USE_SSS
@@ -155,17 +172,23 @@ vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id)
 }
 
 void closure_load_ssr_data(
-    vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl)
+    vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl, vec3 rgb_diffuse, float ao)
 {
-  /* Still encode to avoid artifacts in the SSR pass. */
+  /* Packed colors to a 32bit texture - avoids few fetches, but would make more sense to use separate 16 bit buffers or at least pack before writing to buffer */
+  /* A for Spec, B for Diffuse */
+  /* color inputs over 1.0 are valid and should be allowed - so using clamped range of 0.0 - 10.0 */
+
+  /* Still encode normals to avoid artifacts in the SSR pass. */
   vec3 vN = normalize(mat3(ViewMatrix) * N);
   cl.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition));
 
+  cl.ssr_data = packVec4(vec4(ssr_radiance, roughness), vec4(rgb_diffuse, ao));
   if (ssrToggle && int(ssr_id) == outputSsrId) {
-    cl.ssr_data = vec4(ssr_radiance, roughness);
+    cl.ssr_data = packVec4(vec4(ssr_radiance, roughness), vec4(rgb_diffuse, ao));
     cl.flag |= CLOSURE_SSR_FLAG;
   }
   else {
+    cl.ssr_data = packVec4(vec4(0.0,0.0,0.0, 0.0), vec4(rgb_diffuse, ao));
     cl.radiance += ssr_radiance;
   }
 }
diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
index a6c9eebaff2..9e14d38ecc3 100644
--- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
@@ -46,6 +46,15 @@ layout(std140) uniform common_block
   float alphaHashScale;
   float pad7;
   float pad8;
+  /* Custom MINE GI */
+  float ssrDiffuseIntensity;                        /* float */
+  float ssrDiffuseThickness;                        /* float */
+  float ssrDiffuseTraceBias;                        /* float */
+  float ssrDiffuseResolveBias;                      /* float */
+  float ssrDiffuseEnable;                           /* float */
+  float ssrDiffuseQuality;                          /* float */
+  float ssrDiffuseClamp;                            /* float */
+  float ssrDiffuseAo;                           /* float */
 };
 
 /* rayType (keep in sync with ray_type) */
diff --git a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
index ecff28dcd38..c50d9a496da 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
@@ -7,6 +7,7 @@
 #pragma BLENDER_REQUIRE(raytrace_lib.glsl)
 #pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
 #pragma BLENDER_REQUIRE(ssr_lib.glsl)
+#pragma BLENDER_REQUIRE(common_colorpacking_lib.glsl)
 
 /* Based on Stochastic Screen Space Reflections
  * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */
@@ -41,6 +42,8 @@ uniform sampler2D specroughBuffer;
 
 layout(location = 0) out ivec2 hitData;
 layout(location = 1) out float pdfData;
+layout(location = 2) out ivec2 hitDataGI;
+layout(location = 3) out float pdfDataGI;
 
 void do_planar_ssr(
     int index, vec3 V, vec3 N, vec3 T, vec3 B, vec3 planeNormal, vec3 vP, float a2, vec4 rand)
@@ -109,6 +112,47 @@ void do_ssr(vec3 V, vec3 N, vec3 T, vec3 B, vec3 vP, float a2, vec4 rand)
   hitData = encode_hit_data(hit_pos.xy, (hit_pos.z > 0.0), false);
 }
 
+void do_ssrGI(vec3 V, vec3 N, vec3 T, vec3 B, vec3 viewPosition, float a2, vec4 rand)
+{
+  float NH;
+  /* Microfacet normal */
+  vec3 H = sample_ggx(rand.xzw, a2, N, T, B, NH); //TODO - Switch to constant + noise.
+  //vec3 H = rand.xzw;
+  vec3 R = reflect(-V, H);
+
+  /* If ray is bad (i.e. going below the surface) regenerate. */
+  /* This threshold is a bit higher than 0 to improve self intersection cases. */
+  /* TODO - Bias to view */
+
+  const float bad_ray_threshold = 0.085;
+  if (dot(R, N) <= bad_ray_threshold) {
+    H = sample_ggx(rand.xzw * vec3(1.0, -1.0, -1.0), a2, N, T, B, NH);
+    R = reflect(-V, H);
+  }
+
+  if (dot(R, N) <= bad_ray_threshold) {
+    H = sample_ggx(rand.xzw * vec3(1.0, 1.0, -1.0), a2, N, T, B, NH);
+    R = reflect(-V, H);
+  }
+
+  if (dot(R, N) <= bad_ray_threshold) {
+    H = sample_ggx(rand.xzw * vec3(1.0, -1.0, 1.0), a2, N, T, B, NH);
+    R = reflect(-V, H);
+  }
+
+  if (dot(R, N) <= bad_ray_threshold) {
+    /* Not worth tracing. */
+    return;
+  }
+
+  pdfDataGI = min(1024e32, pdf_ggx_reflect(NH, a2)); /* Theoretical limit of 16bit float */
+
+  vec3 hit_pos = raycast(-1, viewPosition, R * 1e16, ssrDiffuseThickness, rand.y, ssrDiffuseQuality, a2, true);
+
+  hitDataGI = encode_hit_data(hit_pos.xy, (hit_pos.z > 0.0), false);
+}
+
+
 void main()
 {
 #  ifdef FULLRES
@@ -124,6 +168,8 @@ void main()
   /* Default: not hits. */
   hitData = encode_hit_data(vec2(0.5), false, false);
   pdfData = 0.0;
+  hitDataGI = encode_hit_data(vec2(0.5), false, false);
+  pdfDataGI = 0.0;
 
   /* Early out */
   /* We can't do discard because we don't clear the render target. */
@@ -143,22 +189,30 @@ void main()
 
   /* Retrieve pixel data */
   vec4 speccol_roughness = texelFetch(specroughBuffer, fullres_texel, 0).rgba;
+  /* unpack - 1 for Spec, 2 for Diffuse */
+  vec4 difcol_roughness = vec4(0.0);
+  unpackVec4(speccol_roughness, speccol_roughness, difcol_roughness);
 
-  /* Early out */
-  if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) {
-    return;
-  }
+  /* Disable trace if input color total is 0.0 */
+  bool doSpecularSSR = true;
+  bool doDiffuseSSR = true;
+  if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) { doSpecularSSR = false; }
+  if (dot(difcol_roughness.rgb * difcol_roughness.a, vec3(1.0)) == 0.0) { doDiffuseSSR = false; }
 
   float roughness = speccol_roughness.a;
   float roughnessSquared = max(1e-3, roughness * roughness);
   float a2 = roughnessSquared * roughnessSquared;
 
+  /* Skip Specular SSR based on roughness cutoff */
+  if (roughness > ssrMaxRoughness + 0.2) { doSpecularSSR = false; }
+  if (ssrDiffuseIntensity == 0.0) { doDiffuseSSR = false; }
   /* Early out */
-  if (roughness > ssrMaxRoughness + 0.2) {
+  if (doSpecularSSR == false && doDiffuseSSR == false) {
     return;
   }
 
   vec4 rand = texelfetch_noise_tex(halfres_texel);
+  vec4 randGI = rand;
 
   /* Gives *perfect* reflection for very small roughness */
   if (roughness < 0.04) {
@@ -194,7 +248,12 @@ void main()
    * From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
   vP.z = get_view_z_from_depth(depth - mix(2.4e-7, 4.8e-7, depth));
 
-  do_ssr(vV, vN, vT, vB, vP, a2, rand);
+  if (doSpecularSSR == true) {
+    do_ssr(vV, vN, vT, vB, vP, a2, rand);
+  }
+  if (doDiffuseSSR == true) {
+    do_ssrGI(vV, vN, vT, vB, vP, 1.0, randGI);
+  }
 }
 
 #else /* STEP_RESOLVE */
@@ -205,6 +264,8 @@ uniform sampler2D specroughBuffer;
 
 uniform isampler2D hitBuffer;
 uniform sampler2D pdfBuffer;
+uniform isampler2D hitBufferGI;
+uniform sampler2D pdfBufferGI;
 
 uniform int neighborOffset;
 
@@ -469,6 +530,146 @@ vec4 get_ssr_samples(vec4 hit_pdf,
   return accum;
 }
 
+vec4 get_ssr_samplesGI(vec4 hit_pdf,
+                     ivec4 hit_data[2],
+                     PlanarData pd,
+                     float planar_index,
+                     vec3 worldPosition,
+                     vec3 N,
+                     vec3 V,
+                     float roughnessSquared,
+                     float cone_tan,
+                     vec2 source_uvs,
+                     inout float weight_acc)
+{
+
+  bvec4 is_planar, has_hit;
+  vec4 hit_co[2];
+  hit_co[0].xy = decode_hit_data(hit_data[0].xy, has_hit.x, is_planar.x);
+  hit_co[0].zw = decode_hit_data(hit_data[0].zw, has_hit.y, is_planar.y);
+  hit_co[1].xy = decode_hit_data(hit_data[1].xy, has_hit.z, is_planar.z);
+  hit_co[1].zw = decode_hit_data(hit_data[1].zw, has_hit.w, is_planar.w);
+
+  vec4 hit_depth;
+  hit_depth.x = get_sample_depth(hit_co[0].xy, is_planar.x, planar_index);
+  hit_depth.y = get_sample_depth(hit_co[0].zw, is_planar.y, planar_index);
+  hit_depth.z = get_sample_depth(hit_co[1].xy, is_planar.z, planar_index);
+  hit_depth.w = get_sample_depth(hit_co[1].zw, is_planar.w, planar_index);
+
+  /* Hit position in view space. */
+  vec3 hit_view[4];
+  hit_view[0] = get_view_space_from_depth(hit_co[0].xy, hit_depth.x);
+  hit_view[1] = get_view_space_from_depth(hit_co[0].zw, hit_depth.y);
+  hit_view[2] = get_view_space_from_depth(hit_co[1].xy, hit_depth.z);
+  hit_view[3] = get_view_space_from_depth(hit_co[1].zw, hit_depth.w);
+
+  vec4 homcoord = vec4(hit_view[0].z, hit_view[1].z, hit_view[2].z, hit_view[3].z);
+  homcoord = ProjectionMatrix[2][3] * homcoord + ProjectionMatrix[3][3];
+
+  /* Hit position in world space. */
+  vec3 hit_pos[4];
+  hit_pos[0] = transform_point(ViewMatrixInverse, hit_view[0]);
+  hit_pos[1] = transform_point(ViewMatrixInverse, hit_view[1]);
+  hit_pos[2] = transform_point(ViewMatrixInverse, hit_view[2]);
+  hit_pos[3] = transform_point(ViewMatrixInverse, hit_view[3]);
+
+  vec4 mask = vec4(1.0);
+  hit_pos[0] = get_hit_vector(
+      hit_pos[0], pd, worldPosition, N, V, is_planar.x, hit_co[0].xy, mask.x);
+  hit_pos[1] = get_hit_vector(
+      hit_pos[1], pd, worldPosition, N, V, is_planar.y, hit_co[0].zw, mask.y);
+  hit_pos[2] = get_hit_vector(
+      hit_pos[2], pd, worldPosition, N, V, is_planar.z, hit_co[1].xy, mask.z);
+  hit_pos[3] = get_hit_vector(
+      hit_pos[3], pd, worldPosition, N, V, is_planar.w, hit_co[1].zw, mask.w);
+
+  vec4 hit_dist;
+  hit_dist.x = length(hit_pos[0]);
+  hit_dist.y = length(hit_pos[1]);
+  hit_dist.z = length(hit_pos[2]);
+  hit_dist.w = length(hit_pos[3]);
+  hit_dist = max(vec4(1e-8), hit_dist);
+
+  /* Normalize */
+  hit_pos[0] /= hit_dist.x;
+  hit_pos[1] /= hit_dist.y;
+  hit_pos[2] /= hit_dist.z;
+  hit_pos[3] /= hit_dist.w;
+
+  /* Compute cone footprint in screen space. */ /* TODO - Values for conetrace */
+  vec4 cone_footprint = hit_dist * cone_tan;
+  cone_footprint = ssrDiffuseResolveBias * 0.5 * cone_footprint *
+                   max(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) / homcoord;
+
+  /* Estimate a cone footprint to sample a corresponding mipmap level. */
+  vec4 mip = log2(cone_footprint * max_v2(vec2(textureSize(depthBuffer, 0))));
+  mip = clamp(mip, 0.0, MAX_MIP);
+  /* Correct UVs for mipmaping mis-alignment */
+  hit_co[0].xy *= mip_ratio_interp(mip.x);
+  hit_co[0].zw *= mip_ratio_interp(mip.y);
+  hit_co[1].xy *= mip_ratio_interp(mip.z);
+  hit_co[1].zw *= mip_ratio_interp(mip.w);
+
+  /* Slide 54 */ //TODO - constant
+  vec4 bsdf;
+  bsdf.x = bsdf_ggx(N, hit_pos[0], V, roughnessSquared);
+  bsdf.y = bsdf_ggx(N, hit_pos[1], V, roughnessSquared);
+  bsdf.z = bsdf_ggx(N, hit_pos[2], V, roughnessSquared);
+  bsdf.w = bsdf_ggx(N, hit_pos[3], V, roughnessSquared);
+
+  vec4 weight = step(1e-8, hit_pdf) * bsdf / max(vec4(1e-8), hit_pdf);
+
+  vec3 sample[4];
+  sample[0] = get_scene_color(hit_co[0].xy, mip.x, planar_index, is_planar.x);
+  sample[1] = get_scene_color(hit_co[0].zw, mip.y, planar_index, is_planar.y);
+  sample[2] = get_scene_color(hit_co[1].xy, mip.z, planar_index, is_planar.z);
+  sample[3] = get_scene_color(hit_co[1].zw, mip.w, planar_index, is_planar.w);
+
+  /* Clamped brightness. */
+  vec4 luma;
+  luma.x = brightness(sample[0]);
+  luma.y = brightness(sample[1]);
+  luma.z = brightness(sample[2]);
+  luma.w = brightness(sample[3]);
+  luma = max(vec4(1e-8), luma);
+  luma = 1.0 - max(vec4(0.0), luma - ssrDiffuseClamp) / luma;
+
+  sample[0] *= luma.x;
+  sample[1] *= luma.y;
+  sample[2] *= luma.z;
+  sample[3] *= luma.w;
+
+  /* Protection against NaNs in the history buffer.
+   * This could be removed if some previous pass has already
+   * sanitized the input. */
+  if (any(isnan(sample[0]))) {
+    sample[0] = vec3(0.0);
+    weight.x = 0.0;
+  }
+  if (any(isnan(sample[1]))) {
+    sample[1] = vec3(0.0);
+    weight.y = 0.0;
+  }
+  if (any(isnan(sample[2]))) {
+    sample[2] = vec3(0.0);
+    weight.z = 0.0;
+  }
+  if (any(isnan(sample[3]))) {
+    sample[3] = vec3(0.0);
+    weight.w = 0.0;
+  }
+
+  weight_acc += sum(weight);
+
+  /* Do not add light if ray has failed. */
+  vec4 accum;
+  accum = vec4(sample[0], mask.x) * weight.x * float(has_hit.x);
+  accum += vec4(sample[1], mask.y) * weight.y * float(has_hit.y);
+  accum += vec4(sample[2], mask.z) * weight.z * float(has_hit.z);
+  accum += vec4(sample[3], mask.w) * weight.w * float(has_hit.w);
+  return accum;
+}
+
 void raytrace_resolve(ClosureInputGlossy cl_in,
                       inout ClosureEvalGlossy cl_eval,
                       inout ClosureEvalCommon cl_common,
@@ -549,6 +750,86 @@ void raytrace_resolve(ClosureInputGlossy cl_in,
   cl_common.specular_accum -= ssr_accum.a;
 }
 
+vec4 raytrace_resolve_diffuse(vec4 difcol_roughness, out vec4 diffuse_resolve)
+{
+    ivec2 fullres_texel = ivec2(gl_FragCoord.xy);
+  #  ifdef FULLRES
+    ivec2 texel = ivec2(gl_FragCoord.xy);
+  #  else
+    ivec2 texel = ivec2(gl_FragCoord.xy / 2.0);
+  #  endif
+    vec2 uvs = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0));
+
+  /* TODO - Clean up */
+  float depth = textureLod(depthBuffer, uvs, 0.0).r;
+  /* Using world space */
+  vec3 viewPosition = get_view_space_from_depth(uvs, depth); /* Needed for viewCameraVec */
+  vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
+  vec3 V = cameraVec(worldPosition);
+  vec3 vN = normal_decode(texelFetch(normalBuffer, fullres_texel, 0).rg, viewCameraVec(worldPosition));
+  vec3 N = transform_direction(ViewMatrixInverse, vN);
+
+
+  float roughnessSquaredGI = 1.0;
+
+  vec4 spec_accumGI = vec4(0.0);
+
+  /* Resolve SSR */
+  float cone_cosGI = cone_cosine(roughnessSquaredGI); //TODO - Constant
+  float cone_tanGI = sqrt(1 - cone_cosGI * cone_cosGI) / cone_cosGI; //TODO - Constant
+
+  vec2 source_uvs = project_point(pastViewProjectionMatrix, worldPosition).xy * 0.5 + 0.5;
+
+  vec4 ssr_accumGI = vec4(0.0);
+  float weight_accGI = 0.0;
+
+  vec4 hit_pdfGI;
+  hit_pdfGI.x = texelFetch(pdfBufferGI, texel + neighbors[0 + neighborOffset], 0).r;
+  hit_pdfGI.y = texelFetch(pdfBufferGI, texel + neighbors[1 + neighborOffset], 0).r;
+  hit_pdfGI.z = texelFetch(pdfBufferGI, texel + neighbors[2 + neighborOffset], 0).r;
+  hit_pdfGI.w = texelFetch(pdfBufferGI, texel + neighbors[3 + neighborOffset], 0).r;
+
+  ivec4 hit_dataGI[2];
+  hit_dataGI[0].xy = texelFetch(hitBufferGI, texel + neighbors[0 + neighborOffset], 0).rg;
+  hit_dataGI[0].zw = texelFetch(hitBufferGI, texel + neighbors[1 + neighborOffset], 0).rg;
+  hit_dataGI[1].xy = texelFetch(hitBufferGI, texel + neighbors[2 + neighborOffset], 0).rg;
+  hit_dataGI[1].zw = texelFetch(hitBufferGI, texel + neighbors[3 + neighborOffset], 0).rg;
+
+  /* Find Planar Reflections affecting this pixel */ //TODO - Skip all planar for diffuse
+  PlanarData pdGI;
+  float planar_indexGI;
+  for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
+    pdGI = planars_data[i];
+
+    float fadeGI = probe_attenuation_planar(pdGI, worldPosition);
+    fadeGI *= probe_attenuation_planar_normal_roughness(pdGI, N, 0.0);
+
+    if (fadeGI > 0.5) {
+      planar_indexGI = float(i);
+      break;
+    }
+  }
+
+  ssr_accumGI += get_ssr_samplesGI(hit_pdfGI,
+                                hit_dataGI,
+                                pdGI, //Skip
+                                planar_indexGI, //planar_index //Skip
+                                worldPosition, //
+                                N, //
+                                V, //
+                                roughnessSquaredGI,
+                                cone_tanGI,
+                                source_uvs, //
+                                weight_accGI);
+
+  /* diffuse contribution */
+  ssr_accumGI *= (weight_accGI == 0.0) ? 0.0 : (1.0 / weight_accGI); //ssr_accumGI /= weight_accGI;
+  accumulate_light(ssr_accumGI.rgb, ssr_accumGI.a, spec_accumGI); //light, fac, inout accum --- accum += vec4(light, 1.0) * min(fac, (1.0 - accum.a));
+
+  //TODO - Should assume lambertian brdf for simplicity - constant * 0.314 empirically too dim
+  diffuse_resolve = vec4(spec_accumGI.rgb * (difcol_roughness.rgb * mix(1.0,difcol_roughness.a,ssrDiffuseAo) * ssrDiffuseIntensity), 1.0);
+  return diffuse_resolve;
+}
 CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy)
 
 void main()
@@ -561,33 +842,58 @@ void main()
   }
 
   vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba;
+  /* unpack A for Spec, B for Diffuse */
+  vec4 difcol_roughness = vec4(0.0);
+  unpackVec4(speccol_roughness, speccol_roughness, difcol_roughness);
+
   vec3 brdf = speccol_roughness.rgb;
   float roughness = speccol_roughness.a;
 
-  if (max_v3(brdf) <= 0.0) {
+  /* skips */
+  bool skip_spec = false;
+  bool skip_diffuse = false;
+  if (max_v3(brdf) <= 0.0) { skip_spec = true; }
+  if (max_v3(difcol_roughness.rgb * difcol_roughness.a) <= 0.0) { skip_diffuse = true; }
+  if (skip_spec == true && skip_diffuse == true) {
     discard;
   }
 
-  FragDepth = depth;
 
-  viewPosition = get_view_space_from_depth(uvcoordsvar.xy, depth);
-  worldPosition = transform_point(ViewMatrixInverse, viewPosition);
+  /* Specular Resolve - cheaper to branch */
+  vec4 specular_resolve = vec4(0.0);
+  if (skip_spec == false) {
+    FragDepth = depth;
+
+    viewPosition = get_view_space_from_depth(uvcoordsvar.xy, depth);
+    worldPosition = transform_point(ViewMatrixInverse, viewPosition);
+
+    vec2 normal_encoded = texelFetch(normalBuffer, texel, 0).rg;
+    viewNormal = normal_decode(normal_encoded, viewCameraVec(viewPosition));
+    worldNormal = transform_direction(ViewMatrixInverse, viewNormal);
 
-  vec2 normal_encoded = texelFetch(normalBuffer, texel, 0).rg;
-  viewNormal = normal_decode(normal_encoded, viewCameraVec(viewPosition));
-  worldNormal = transform_direction(ViewMatrixInverse, viewNormal);
+    CLOSURE_VARS_DECLARE_1(Glossy);
 
-  CLOSURE_VARS_DECLARE_1(Glossy);
+    in_Glossy_0.N = worldNormal;
+    in_Glossy_0.roughness = roughness;
 
-  in_Glossy_0.N = worldNormal;
-  in_Glossy_0.roughness = roughness;
+    /* Do a full deferred evaluation of the glossy BSDF. The only difference is that we inject the
+    * SSR resolve before the cubemap iter. BRDF term is already computed during main pass and is
+    * passed as specular color. */
+    CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy);
+
+    specular_resolve = vec4(out_Glossy_0.radiance * brdf, 1.0);
+  }
 
-  /* Do a full deferred evaluation of the glossy BSDF. The only difference is that we inject the
-   * SSR resolve before the cubemap iter. BRDF term is already computed during main pass and is
-   * passed as specular color. */
-  CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy);
+  /* Diffuse Resolve */
+  vec4 diffuse_resolve = vec4(0.0);
+  if (skip_diffuse == false) { diffuse_resolve = raytrace_resolve_diffuse(difcol_roughness, diffuse_resolve); }
 
-  fragColor = vec4(out_Glossy_0.radiance * brdf, 1.0);
+  /* TODO separate spec and diffuse, write diffuse to buffer */
+  fragColor = vec4(
+  (specular_resolve.rgb)
+  +
+  (diffuse_resolve.rgb)
+  , 1.0);
 }
 
 #endif
diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
index 62930b9944a..79c91a8383d 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
@@ -13,6 +13,8 @@
 #pragma BLENDER_REQUIRE(surface_lib.glsl)
 #pragma BLENDER_REQUIRE(volumetric_lib.glsl)
 
+#pragma BLENDER_REQUIRE(common_colorpacking_lib.glsl)
+
 #ifdef USE_ALPHA_BLEND
 /* Use dual source blending to be able to make a whole range of effects. */
 layout(location = 0, index = 0) out vec4 outRadiance;
@@ -81,7 +83,12 @@ void main()
 #ifndef USE_ALPHA_BLEND
   float alpha_div = 1.0 / max(1e-8, alpha);
   outRadiance.rgb *= alpha_div;
-  ssrData.rgb *= alpha_div;
+  /* unpack TODO - clean up - Needs Vec3 only*/
+  vec4 spec = vec4(0.0);
+  vec4 diffuse = vec4(0.0);
+  unpackVec4(ssrData.rgba, spec, diffuse);
+
+  ssrData.rgb = packVec3(spec.rgb * alpha_div, diffuse.rgb * alpha_div);
 #  ifdef USE_SSS
   sssAlbedo.rgb *= alpha_div;
 #  endif
diff --git a/source/blender/draw/intern/shaders/common_colorpacking_lib.glsl b/source/blender/draw/intern/shaders/common_colorpacking_lib.glsl
new file mode 100644
index 00000000000..36ee11e204b
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_colorpacking_lib.glsl
@@ -0,0 +1,68 @@
+/*****************************************/
+/* 2 component packing */
+
+const float c_precision = 4096.0; //4096 (12bit * 2)
+const float c_precisionp1 = c_precision + 1.0;
+const float range = 0.1;
+const float range_inverse = 10.0;
+
+vec4 packVec4(vec4 color_a, vec4 color_b) {
+	vec4 packed_color = vec4(0.0);
+	/* Clamp inputs*/
+	color_a *= range;
+	color_a = clamp(color_a, 0.0, 1.0);
+    color_b *= range;
+    color_b = clamp(color_b, 0.0, 1.0);
+	/* Pack RGBA */
+	packed_color.r = floor(color_a.r * c_precision + 0.5) + floor(color_b.r * c_precision + 0.5) * c_precisionp1;
+    packed_color.g = floor(color_a.g * c_precision + 0.5) + floor(color_b.g * c_precision + 0.5) * c_precisionp1;
+    packed_color.b = floor(color_a.b * c_precision + 0.5) + floor(color_b.b * c_precision + 0.5) * c_precisionp1;
+    packed_color.a = floor(color_a.a * c_precision + 0.5) + floor(color_b.a * c_precision + 0.5) * c_precisionp1;
+
+    return packed_color.rgba;
+}
+
+vec3 packVec3(vec3 color_a, vec3 color_b) {
+	vec3 packed_color = vec3(0.0);
+	/* Clamp inputs*/
+	color_a *= range;
+	color_a = clamp(color_a, 0.0, 1.0);
+    color_b *= range;
+    color_b = clamp(color_b, 0.0, 1.0);
+	/* Pack RGB */
+	packed_color.r = floor(color_a.r * c_precision + 0.5) + floor(color_b.r * c_precision + 0.5) * c_precisionp1;
+    packed_color.g = floor(color_a.g * c_precision + 0.5) + floor(color_b.g * c_precision + 0.5) * c_precisionp1;
+    packed_color.b = floor(color_a.b * c_precision + 0.5) + floor(color_b.b * c_precision + 0.5) * c_precisionp1;
+
+    return packed_color.rgb;
+}
+
+float packFloat(float color_a, float color_b) {
+	float packed_color = 0.0;
+	/* Clamp inputs*/
+	color_a *= range;
+	color_a = clamp(color_a, 0.0, 1.0);
+    color_b *= range;
+    color_b = clamp(color_b, 0.0, 1.0);
+	/* Pack float */
+	packed_color = floor(color_a * c_precision + 0.5) + floor(color_b * c_precision + 0.5) * c_precisionp1;
+
+    return packed_color;
+}
+
+vec4 unpackVec4(vec4 packedColor, out vec4 color_a, out vec4 color_b){
+	vec4 color;
+	color_a.r = mod(packedColor.r, c_precisionp1) / c_precision;
+	color_b.r = mod(floor(packedColor.r / c_precisionp1), c_precisionp1) / c_precision;
+    color_a.g = mod(packedColor.g, c_precisionp1) / c_precision;
+	color_b.g = mod(floor(packedColor.g / c_precisionp1), c_precisionp1) / c_precision;
+    color_a.b = mod(packedColor.b, c_precisionp1) / c_precision;
+	color_b.b = mod(floor(packedColor.b / c_precisionp1), c_precisionp1) / c_precision;
+    color_a.a = mod(packedColor.a, c_precisionp1) / c_precision;
+	color_b.a = mod(floor(packedColor.a / c_precisionp1), c_precisionp1) / c_precision;
+	/* Restore range */
+	color_a *= range_inverse;
+	color_b *= range_inverse;
+
+	return vec4(0.0);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
index 01a16e194ca..f7b242771f5 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
@@ -5,21 +5,19 @@ CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse)
 void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
 {
   CLOSURE_VARS_DECLARE_1(Diffuse);
-
   in_Diffuse_0.N = N; /* Normalized during eval. */
   in_Diffuse_0.albedo = color.rgb;
 
   CLOSURE_EVAL_FUNCTION_1(node_bsdf_diffuse, Diffuse);
 
   result = CLOSURE_DEFAULT;
-
   out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1.0), out_Diffuse_0.radiance);
   out_Diffuse_0.radiance *= color.rgb;
 
   result.radiance = out_Diffuse_0.radiance;
 
   /* TODO(fclem) Try to not use this. */
-  closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
+  closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result, color.rgb, out_Diffuse_0.AO);
 }
 
 #else
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
index 0941482df45..2ae3332111e 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
@@ -50,7 +50,7 @@ void node_eevee_specular(vec4 diffuse,
     out_Glossy_1.radiance *= brdf;
     out_Glossy_1.radiance = render_pass_glossy_mask(specular.rgb, out_Glossy_1.radiance);
     closure_load_ssr_data(
-        out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
+        out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result, diffuse.rgb, out_Diffuse_0.AO);
   }
   {
     /* Clearcoat. */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
index aa0a8873596..e9c997e84ed 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
@@ -38,7 +38,7 @@ void node_bsdf_glass(vec4 color,
   out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
   out_Glossy_0.radiance *= color.rgb * fresnel;
   closure_load_ssr_data(
-      out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
+      out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result, vec3(0.0), 1.0);
 
   float btdf = (do_multiscatter != 0.0) ?
                    1.0 :
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
index fa83bfb6c7a..b4bf3dbead0 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
@@ -24,7 +24,7 @@ void node_bsdf_glossy(
   out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
   out_Glossy_0.radiance *= color.rgb;
   closure_load_ssr_data(
-      out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
+      out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result, vec3(0.0), 1.0);
 }
 
 #else
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index 49c8973a8ce..0c639afac8e 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -140,7 +140,7 @@ void node_bsdf_principled(vec4 base_color,
     }
 
     closure_load_ssr_data(
-        glossy_radiance_final, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
+        glossy_radiance_final * alpha, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result, (mix(base_color.rgb, subsurface_color.rgb, subsurface) * alpha) * (1 - metallic), out_Diffuse_0.AO);
   }
 
   if (diffuse_weight > 1e-5) {
@@ -191,7 +191,7 @@ void node_bsdf_principled(vec4 base_color,
 
   result.transmittance = vec3(1.0 - alpha);
   result.radiance *= alpha;
-  result.ssr_data.rgb *= alpha;
+  //result.ssr_data.rgb *= alpha;
 #  ifdef USE_SSS
   result.sss_irradiance *= alpha;
 #  endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
index 5a68f802659..f7edcd4e49e 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
@@ -31,7 +31,7 @@ void node_subsurface_scattering(vec4 color,
   closure_load_sss_data(scale, out_Diffuse_0.radiance, sss_albedo, int(sss_id), result);
 
   /* TODO(fclem) Try to not use this. */
-  closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
+  closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result, color.rgb, out_Diffuse_0.AO);
 }
 
 #else
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
index 80bd3941b22..c2dc6f3dcb6 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
@@ -11,7 +11,7 @@ void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
   CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent);
 
   result = CLOSURE_DEFAULT;
-  closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result);
+  closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result, color.rgb , 1.0);
   result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb);
 }
 
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 3e1b2ef56a1..8fc3826871f 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -195,6 +195,14 @@
     .ssr_thickness = 0.2f, \
     .ssr_border_fade = 0.075f, \
     .ssr_firefly_fac = 10.0f, \
+    .ssr_diffuse_thickness = 1.0f, \
+    .ssr_diffuse_intensity = 1.0f, \
+    .ssr_diffuse_trace_bias = 0.0f, \
+    .ssr_diffuse_resolve_bias = 0.1f, \
+    .ssr_diffuse_enable = 1.0f, \
+    .ssr_diffuse_quality = 0.25f, \
+    .ssr_diffuse_clamp = 1.0f, \
+    .ssr_diffuse_ao = 1.0f, \
  \
     .volumetric_start = 0.1f, \
     .volumetric_end = 100.0f, \
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c7f7e610a1a..516d65b35d0 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1613,6 +1613,14 @@ typedef struct SceneEEVEE {
   float ssr_thickness;
   float ssr_border_fade;
   float ssr_firefly_fac;
+  float ssr_diffuse_intensity;
+  float ssr_diffuse_thickness;
+  float ssr_diffuse_trace_bias;
+  float ssr_diffuse_resolve_bias;
+  float ssr_diffuse_enable;
+  float ssr_diffuse_quality;
+  float ssr_diffuse_clamp;
+  float ssr_diffuse_ao;
 
   float volumetric_start;
   float volumetric_end;
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index b9fc89db0d1..f651ba348f6 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -7121,6 +7121,57 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
   RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
   RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
 
+  prop = RNA_def_property(srna, "ssr_diffuse_intensity", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_ui_text(prop, "Intensity", "Diffuse bounce light intensity - 0.0 disables all tracing and resolve in shader");
+  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_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+  prop = RNA_def_property(srna, "ssr_diffuse_thickness", PROP_FLOAT, PROP_DISTANCE);
+  RNA_def_property_ui_text(prop, "Thickness", "Pixel thickness used to detect intersection for diffuse bounce");
+  RNA_def_property_range(prop, 0.0f, FLT_MAX);
+  RNA_def_property_ui_range(prop, 0.0f, 3.0f, 5, 3);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+  RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+  prop = RNA_def_property(srna, "ssr_diffuse_trace_bias", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_ui_text(prop, "Trace Bias", "Diffuse Tracing BSDF Bias");
+  RNA_def_property_range(prop, 0.0f, 0.1f);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+  RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+  prop = RNA_def_property(srna, "ssr_diffuse_resolve_bias", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_ui_text(prop, "Resolve Bias", "Diffuse resolve bias - Higher values blur ray hit color sample, potentially reducing noise at the cost of accuracy. Can introduce noticable light bleeding.");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+  RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+  prop = RNA_def_property(srna, "ssr_diffuse_enable", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_ui_text(prop, "Enable SSGI", " Enable diffuse raytracing for screen space global illumination");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+  RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+  prop = RNA_def_property(srna, "ssr_diffuse_quality", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_ui_text(prop, "Ray Length", "Diffuse trace ray length");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+  RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+  prop = RNA_def_property(srna, "ssr_diffuse_clamp", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_ui_text(prop, "Clamp", "Firefly filter for diffuse resolve");
+  RNA_def_property_range(prop, 0.0f, FLT_MAX);
+  RNA_def_property_ui_range(prop, 0.0f, 1.0f, 5, 3);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+  RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+  prop = RNA_def_property(srna, "ssr_diffuse_ao", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_ui_text(prop, "Occlusion", "Multiply SSGI output with AO");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  //RNA_def_property_ui_range(prop, 0.0f, 1.0f, 5, 3);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+  RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
   /* Volumetrics */
   prop = RNA_def_property(srna, "volumetric_start", PROP_FLOAT, PROP_DISTANCE);
   RNA_def_property_ui_text(prop, "Start", "Start distance of the volumetric effect");
