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:
parent
5ccc02277d
commit
779c950098
|
@ -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)
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue