Eevee: Ambient Occlusion: Initial implementation.

Implement GTAO (Ground Truth Ambient Occlusion) which is a special case of Horizon Based Ambient Occlusion that is more physically accurate.
Also add a bent normal option to sample indirect irradiance (diffuse lighting) with the least occluded direction.
This commit is contained in:
Clément Foucault 2017-06-22 03:10:39 +02:00
parent 5ccc02277d
commit 779c950098
8 changed files with 277 additions and 11 deletions

View File

@ -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)

View File

@ -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};

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;