diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 7c4b5428d75..eb3a016f295 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -118,6 +118,7 @@ data_to_c_simple(engines/clay/shaders/ssao_groundtruth.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC) +data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index e7fa51ad929..0aa72f6bc2c 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -386,6 +386,14 @@ void EEVEE_effects_init(EEVEE_Data *vedata) (int)viewport_size[0], (int)viewport_size[1], &tex, 1); } + + { + /* 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 = 1.0f - BKE_collection_engine_property_value_get_float(props, "gtao_factor"); + } + /* MinMax Pyramid */ /* TODO reduce precision */ DRWFboTexture tex = {&stl->g_data->minmaxz, DRW_TEX_RG_32, DRW_TEX_MIPMAP | DRW_TEX_TEMP}; diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 09c30ff5a69..1043915c894 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -75,6 +75,7 @@ static struct { float viewvecs[2][4]; } e_data = {NULL}; /* Engine data */ +extern char datatoc_ambient_occlusion_lib_glsl[]; extern char datatoc_prepass_frag_glsl[]; extern char datatoc_prepass_vert_glsl[]; extern char datatoc_default_frag_glsl[]; @@ -215,6 +216,13 @@ static void add_standard_uniforms(DRWShadingGroup *shgrp, EEVEE_SceneLayerData * DRW_shgroup_uniform_buffer(shgrp, "irradianceGrid", &sldata->irradiance_pool); DRW_shgroup_uniform_buffer(shgrp, "shadowCubes", &sldata->shadow_depth_cube_pool); DRW_shgroup_uniform_buffer(shgrp, "shadowCascades", &sldata->shadow_depth_cascade_pool); + if (vedata->stl->effects->use_ao) { + DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)e_data.viewvecs, 3); + DRW_shgroup_uniform_buffer(shgrp, "minMaxDepthTex", &vedata->stl->g_data->minmaxz); + DRW_shgroup_uniform_float(shgrp, "aoDistance", &vedata->stl->effects->ao_dist, 1); + DRW_shgroup_uniform_float(shgrp, "aoSamples", &vedata->stl->effects->ao_samples, 1); + DRW_shgroup_uniform_float(shgrp, "aoFactor", &vedata->stl->effects->ao_factor, 1); + } } static void create_default_shader(int options) @@ -232,6 +240,7 @@ static void create_default_shader(int options) MEM_freeN(defines); MEM_freeN(frag_str); } + void EEVEE_materials_init(void) { if (!e_data.frag_shader_lib) { @@ -240,6 +249,7 @@ void EEVEE_materials_init(void) /* Shaders */ 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_octahedron_lib_glsl); BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl); BLI_dynstr_append(ds_frag, datatoc_ltc_lib_glsl); @@ -287,14 +297,57 @@ void EEVEE_materials_init(void) for (int i = 0; i < 64 * 64; i++) { texels_layer[i][0] = blue_noise[i][0]; - texels_layer[i][1] = blue_noise[i][1]; + texels_layer[i][1] = blue_noise[i][1] * 0.5 + 0.5; texels_layer[i][2] = blue_noise[i][2]; texels_layer[i][3] = blue_noise[i][3]; } - e_data.util_tex = DRW_texture_create_2D_array(64, 64, layers, DRW_TEX_RGBA_16, DRW_TEX_FILTER, (float *)texels); + e_data.util_tex = DRW_texture_create_2D_array(64, 64, layers, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_WRAP, (float *)texels); MEM_freeN(texels); } + + { + /* Update viewvecs */ + const bool is_persp = DRW_viewport_is_persp_get(); + float invproj[4][4], winmat[4][4]; + /* view vectors for the corners of the view frustum. + * Can be used to recreate the world space position easily */ + float viewvecs[3][4] = { + {-1.0f, -1.0f, -1.0f, 1.0f}, + {1.0f, -1.0f, -1.0f, 1.0f}, + {-1.0f, 1.0f, -1.0f, 1.0f} + }; + + /* invert the view matrix */ + DRW_viewport_matrix_get(winmat, DRW_MAT_WIN); + invert_m4_m4(invproj, winmat); + + /* convert the view vectors to view space */ + for (int i = 0; i < 3; i++) { + mul_m4_v4(invproj, viewvecs[i]); + /* normalized trick see: + * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ + mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][3]); + if (is_persp) + mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][2]); + viewvecs[i][3] = 1.0; + } + + copy_v4_v4(e_data.viewvecs[0], viewvecs[0]); + copy_v4_v4(e_data.viewvecs[1], viewvecs[1]); + + /* we need to store the differences */ + e_data.viewvecs[1][0] -= viewvecs[0][0]; + e_data.viewvecs[1][1] = viewvecs[2][1] - viewvecs[0][1]; + + /* calculate a depth offset as well */ + if (!is_persp) { + float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f}; + mul_m4_v4(invproj, vec_far); + mul_v3_fl(vec_far, 1.0f / vec_far[3]); + e_data.viewvecs[1][2] = vec_far[2] - viewvecs[0][2]; + } + } } struct GPUMaterial *EEVEE_material_world_lightprobe_get(struct Scene *scene, World *wo) diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index bd9e1bf9e89..2f96da1cf4f 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -273,6 +273,10 @@ enum { typedef struct EEVEE_EffectsInfo { int enabled_effects; + /* Ambient Occlusion */ + bool use_ao, use_bent_normals; + float ao_dist, ao_samples, ao_factor; + /* Motion Blur */ float current_ndc_to_world[4][4]; float past_world_to_ndc[4][4]; @@ -402,7 +406,6 @@ EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob); EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob); EEVEE_LampEngineData *EEVEE_lamp_data_get(Object *ob); - /* eevee_materials.c */ void EEVEE_materials_init(void); void EEVEE_materials_cache_init(EEVEE_Data *vedata); diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl new file mode 100644 index 00000000000..a3a1d989475 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -0,0 +1,153 @@ + +/* Based on Practical Realtime Strategies for Accurate Indirect Occlusion + * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf + * 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 6.0 + +uniform sampler2D minMaxDepthTex; +uniform float aoDistance; +uniform float aoSamples; +uniform float aoFactor; + +float sample_depth(vec2 co, int level) +{ + return textureLod(minMaxDepthTex, co, float(level)).g; +} + +float get_max_horizon(vec2 co, vec3 x, float h, float step) +{ + if (co.x > 1.0 || co.x < 0.0 || co.y > 1.0 || co.y < 0.0) + return h; + + float depth = sample_depth(co, int(step)); + + /* Background case */ + if (depth == 1.0) + return h; + + vec3 s = get_view_space_from_depth(co, depth); /* s View coordinate */ + vec3 omega_s = s - x; + float len = length(omega_s); + + float max_h = max(h, omega_s.z / len); + /* Blend weight after half the aoDistance to fade artifacts */ + float blend = saturate((1.0 - len / aoDistance) * 2.0); + + return mix(h, max_h, blend); +} + +void gtao(vec3 normal, vec3 position, vec2 noise, out float visibility +#ifdef USE_BENT_NORMAL + , out vec3 bent_normal +#endif + ) +{ + vec2 screenres = vec2(textureSize(minMaxDepthTex, 0)) * 2.0; + vec2 pixel_size = vec2(1.0) / screenres.xy; + + /* Renaming */ + vec2 x_ = gl_FragCoord.xy * pixel_size; /* x^ Screen coordinate */ + vec3 x = position; /* x view space coordinate */ + + /* 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); */ + + 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 */ + + /* Integral over PI */ + visibility = 0.0; +#ifdef USE_BENT_NORMAL + bent_normal = vec3(0.0); +#endif + for (float i = 0.0; i < aoSamples && i < MAX_PHI_STEP; i++) { + float phi = M_PI * ((noise.r + i) / aoSamples); + + /* Rotate with random direction to get jittered result. */ + vec2 t_phi = vec2(cos(phi), sin(phi)); /* Screen space direction */ + + /* Search maximum horizon angles h1 and h2 */ + float h1 = -1.0, h2 = -1.0; /* init at cos(pi) */ + float ofs = 1.5 * pixel_len; + for (float j = 0.0; ofs < max_dist && j < MAX_THETA_STEP; j += 0.5) { + ofs += ofs; /* Step size is doubled each iteration */ + + vec2 s_ = t_phi * ofs * noise.g * pixel_ratio; /* s^ Screen coordinate */ + vec2 co; + + co = x_ + s_; + h1 = get_max_horizon(co, x, h1, j); + + co = x_ - s_; + h2 = get_max_horizon(co, x, h2, j); + } + + /* (Slide 54) */ + h1 = -acos(h1); + h2 = acos(h2); + + /* 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 t = vec3(-t_phi, 0.0); + vec3 n_proj = normal - h * dot(h, 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)) * 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); + + /* Solving inner integral */ + float sin_n = sin(n); + float h1_2 = 2.0 * h1; + float h2_2 = 2.0 * h2; + float vd = (-cos(h1_2 - n) + cos_n + h1_2 * sin_n) + (-cos(h2_2 - n) + cos_n + h2_2 * sin_n); + vd *= 0.25 * n_proj_len; + visibility += vd; + +#ifdef USE_BENT_NORMAL + /* Finding Bent normal */ + float b_angle = (h1 + h2) / 2.0; + /* 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 + } + + visibility = min(1.0, visibility / aoSamples); + +#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 = max(0.0, mix(aoFactor, 1.0, visibility)); +} + +/* Multibounce approximation base on surface albedo. + * Page 78 in the .pdf version. */ +float gtao_multibounce(float visibility, vec3 albedo) +{ + /* Median luminance. Because Colored multibounce looks bad. */ + float lum = albedo.x * 0.3333; + lum += albedo.y * 0.3333; + lum += albedo.z * 0.3333; + + float a = 2.0404 * lum - 0.3324; + float b = -4.7951 * lum + 0.6417; + float c = 2.7552 * lum + 0.6903; + + float x = visibility; + return max(x, ((x * a + b) * x + c) * x); +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index 5ea74f38e83..6ce4b2f7501 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -7,6 +7,9 @@ #define LUT_SIZE 64 +uniform mat4 ProjectionMatrix; +uniform vec4 viewvecs[2]; + /* ------- Structures -------- */ struct ProbeData { @@ -248,6 +251,18 @@ float buffer_depth(bool is_persp, float z, float zf, float zn) } } +vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = 2.0 * depth - 1.0; + float zview = -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); + return (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz) * zview; + } + else { + return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz; + } +} + vec3 get_specular_dominant_dir(vec3 N, vec3 R, float roughness) { float smoothness = 1.0 - roughness; @@ -255,6 +270,11 @@ vec3 get_specular_dominant_dir(vec3 N, vec3 R, float roughness) return normalize(mix(N, R, fac)); } +float specular_occlusion(float NV, float AO, float roughness) +{ + return saturate(pow(NV + AO, roughness) - 1.0 + AO); +} + /* Fresnel */ vec3 F_schlick(vec3 f0, float cos_theta) { diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl index 06fcffb6244..44bfc5353ea 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -3,7 +3,7 @@ uniform int light_count; uniform int probe_count; uniform int grid_count; uniform int planar_count; -uniform mat4 ProjectionMatrix; +uniform mat4 ViewMatrix; uniform mat4 ViewMatrixInverse; uniform sampler2DArray probePlanars; @@ -301,6 +301,30 @@ float planar_attenuation(vec3 W, vec3 N, PlanarData pd) return fac; } +float compute_occlusion(vec3 N, float micro_occlusion, vec2 randuv, out vec3 bent_normal) +{ +#ifdef USE_AO /* Screen Space Occlusion */ + + float macro_occlusion; + vec3 vnor = mat3(ViewMatrix) * N; + +#ifdef USE_BENT_NORMAL + gtao(vnor, viewPosition, randuv, macro_occlusion, bent_normal); + bent_normal = mat3(ViewMatrixInverse) * bent_normal; +#else + gtao(vnor, viewPosition, randuv, macro_occlusion); + bent_normal = N; +#endif + return min(macro_occlusion, micro_occlusion); + +#else /* No added Occlusion. */ + + bent_normal = N; + return micro_occlusion; + +#endif +} + vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, float ao) { roughness = clamp(roughness, 1e-8, 0.9999); @@ -342,6 +366,10 @@ vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, sd.N = -norm_view; #endif + vec3 bent_normal; + vec4 rand = textureLod(utilTex, vec3(gl_FragCoord.xy / LUT_SIZE, 2.0), 0.0).rgba; + float final_ao = compute_occlusion(sd.N, ao, rand.rg, bent_normal); + /* Envmaps */ vec3 R = reflect(-sd.V, sd.N); vec3 spec_dir = get_specular_dominant_dir(sd.N, R, roughnessSquared); @@ -431,7 +459,7 @@ vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, /* Avoid zero weight */ weight = max(0.00001, weight); - vec3 color = get_cell_color(ivec3(cell_cos), gd.g_resolution, gd.g_offset, sd.N); + vec3 color = get_cell_color(ivec3(cell_cos), gd.g_resolution, gd.g_offset, bent_normal); weight_accum += weight; irradiance_accum += color * weight; @@ -451,9 +479,9 @@ vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, /* World probe */ if (diff_accum.a < 1.0 && grid_count > 0) { - IrradianceData ir_data = load_irradiance_cell(0, sd.N); + IrradianceData ir_data = load_irradiance_cell(0, bent_normal); - vec3 diff = compute_irradiance(sd.N, ir_data); + vec3 diff = compute_irradiance(bent_normal, ir_data); diff_accum.rgb += diff * (1.0 - diff_accum.a); } @@ -465,8 +493,8 @@ vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, } vec3 indirect_radiance = - spec_accum.rgb * F_ibl(f0, brdf_lut) * float(specToggle) + - diff_accum.rgb * albedo; + spec_accum.rgb * F_ibl(f0, brdf_lut) * float(specToggle) * specular_occlusion(dot(sd.N, sd.V), final_ao, roughness) + + diff_accum.rgb * albedo * final_ao; - return radiance + indirect_radiance * ao; + return radiance + indirect_radiance; } diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 8b2e42a9e65..254779dfbcb 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -3,8 +3,8 @@ uniform mat4 ModelViewMatrix; #ifndef EEVEE_ENGINE uniform mat4 ProjectionMatrix; uniform mat4 ViewMatrixInverse; -#endif uniform mat4 ViewMatrix; +#endif uniform mat4 ModelMatrix; uniform mat4 ModelMatrixInverse; uniform mat4 ModelViewMatrixInverse;