Eevee: Optimize and improve GTAO Horizon search

This fix a bug when occluder are on the edge of the screen and occludes more than they should.

Grouped the texture fetches together and clamp the ray at the border of the screen.

Also add a few util functions.
This commit is contained in:
Clément Foucault 2017-08-13 14:30:24 +02:00
parent 82e0419cc0
commit e3468d7ec2
5 changed files with 88 additions and 83 deletions

View File

@ -385,13 +385,13 @@ static void add_standard_uniforms(
if (vedata->stl->effects->use_ao || use_ssrefraction) {
DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)vedata->stl->g_data->viewvecs, 2);
DRW_shgroup_uniform_buffer(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer);
DRW_shgroup_uniform_vec2(shgrp, "mipRatio[0]", (float *)vedata->stl->g_data->mip_ratio, 10);
}
if (use_ssrefraction) {
DRW_shgroup_uniform_buffer(shgrp, "colorBuffer", &vedata->txl->refract_color);
DRW_shgroup_uniform_vec4(shgrp, "ssrParameters", &vedata->stl->effects->ssr_quality, 1);
DRW_shgroup_uniform_float(shgrp, "borderFadeFactor", &vedata->stl->effects->ssr_border_fac, 1);
DRW_shgroup_uniform_float(shgrp, "maxRoughness", &vedata->stl->effects->ssr_max_roughness, 1);
DRW_shgroup_uniform_vec2(shgrp, "mipRatio[0]", (float *)vedata->stl->g_data->mip_ratio, 10);
DRW_shgroup_uniform_int(shgrp, "rayCount", &vedata->stl->effects->ssr_ray_count, 1);
}
if (vedata->stl->effects->use_ao) {

View File

@ -13,98 +13,102 @@ uniform vec3 aoParameters;
#define aoSamples aoParameters.y
#define aoFactor aoParameters.z
float get_max_horizon(vec2 co, vec3 x, float h, float lod)
void get_max_horizon_grouped(vec4 co1, vec4 co2, vec3 x, float lod, inout float h)
{
float depth = textureLod(maxzBuffer, co, floor(lod)).r;
co1 *= mipRatio[int(lod + 1.0)].xyxy; /* +1 because we are using half res top level */
co2 *= mipRatio[int(lod + 1.0)].xyxy; /* +1 because we are using half res top level */
/* Background case */
/* this is really slow and is only a problem
* if the far clip plane is near enough to notice */
// depth += step(1.0, depth) * 1e20;
float depth1 = textureLod(maxzBuffer, co1.xy, floor(lod)).r;
float depth2 = textureLod(maxzBuffer, co1.zw, floor(lod)).r;
float depth3 = textureLod(maxzBuffer, co2.xy, floor(lod)).r;
float depth4 = textureLod(maxzBuffer, co2.zw, floor(lod)).r;
vec3 s = get_view_space_from_depth(co, depth); /* s View coordinate */
vec3 omega_s = s - x;
float len = length(omega_s);
vec4 len, s_h;
vec3 s1 = get_view_space_from_depth(co1.xy, depth1); /* s View coordinate */
vec3 omega_s1 = s1 - x;
len.x = length(omega_s1);
s_h.x = omega_s1.z / len.x;
vec3 s2 = get_view_space_from_depth(co1.zw, depth2); /* s View coordinate */
vec3 omega_s2 = s2 - x;
len.y = length(omega_s2);
s_h.y = omega_s2.z / len.y;
vec3 s3 = get_view_space_from_depth(co2.xy, depth3); /* s View coordinate */
vec3 omega_s3 = s3 - x;
len.z = length(omega_s3);
s_h.z = omega_s3.z / len.z;
vec3 s4 = get_view_space_from_depth(co2.zw, depth4); /* s View coordinate */
vec3 omega_s4 = s4 - x;
len.w = length(omega_s4);
s_h.w = omega_s4.z / len.w;
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);
vec4 blend = saturate((1.0 - len / aoDistance) * 2.0);
return mix(h, max_h, blend);
h = mix(h, max(h, s_h.x), blend.x);
h = mix(h, max(h, s_h.y), blend.y);
h = mix(h, max(h, s_h.z), blend.z);
h = mix(h, max(h, s_h.w), blend.w);
}
void search_step(
vec2 t_phi, vec3 x, vec2 x_, float rand, vec2 pixel_ratio,
inout float j, inout float ofs, inout float h1, inout float h2)
#define MAX_ITER 16
#define MAX_LOD 6
#define QUALITY 0.75
vec2 search_horizon_sweep(vec2 t_phi, vec3 pos, vec2 uvs, float jitter, vec2 max_dir)
{
ofs += ofs; /* Step size is doubled each iteration */
max_dir *= max_v2(abs(t_phi));
vec2 s_ = t_phi * ofs * rand * pixel_ratio; /* s^ Screen coordinate */
vec2 co;
/* Convert to pixel space. */
t_phi /= vec2(textureSize(maxzBuffer, 0));
co = x_ + s_;
h1 = get_max_horizon(co, x, h1, j);
/* Avoid division by 0 */
t_phi += vec2(1e-5);
co = x_ - s_;
h2 = get_max_horizon(co, x, h2, j);
jitter *= 0.25;
j += 0.5;
}
/* Compute end points */
vec2 corner1 = min(vec2(1.0) - uvs, max_dir); /* Top right */
vec2 corner2 = max(vec2(0.0) - uvs, -max_dir); /* Bottom left */
vec2 iter1 = corner1 / t_phi;
vec2 iter2 = corner2 / t_phi;
void search_horizon(
vec2 t_phi, vec3 x, vec2 x_, float rand,
float max_dist, vec2 pixel_ratio, float pixel_len,
inout float h1, inout float h2)
{
float ofs = 1.5 * pixel_len;
float j = 0.0;
vec2 min_iter = max(-iter1, -iter2);
vec2 max_iter = max( iter1, iter2);
#if 0 /* manually unrolled bellow */
for (int i = 0; i < MAX_THETA_STEP; i++) {
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist)
return;
vec2 times = vec2(-min_v2(min_iter), min_v2(max_iter));
vec2 h = vec2(-1.0); /* init at cos(pi) */
/* This is freaking sexy optimized. */
for (float i = 0.0, ofs = 4.0, time = -1.0;
i < MAX_ITER && time > times.x;
i++, time -= ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs))
{
vec4 t = max(times.xxxx, vec4(time) - (vec4(0.25, 0.5, 0.75, 1.0) - jitter) * ofs);
vec4 cos1 = uvs.xyxy + t_phi.xyxy * t.xxyy;
vec4 cos2 = uvs.xyxy + t_phi.xyxy * t.zzww;
get_max_horizon_grouped(cos1, cos2, pos, min(MAX_LOD, i * QUALITY), h.y);
}
#endif
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
for (float i = 0.0, ofs = 4.0, time = 1.0;
i < MAX_ITER && time < times.y;
i++, time += ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs))
{
vec4 t = min(times.yyyy, vec4(time) + (vec4(0.25, 0.5, 0.75, 1.0) - jitter) * ofs);
vec4 cos1 = uvs.xyxy + t_phi.xyxy * t.xxyy;
vec4 cos2 = uvs.xyxy + t_phi.xyxy * t.zzww;
get_max_horizon_grouped(cos1, cos2, pos, min(MAX_LOD, i * QUALITY), h.x);
}
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
if (ofs > max_dist) return;
search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
return h;
}
void integrate_slice(
float iter, vec3 x, vec3 normal, vec2 x_, vec2 noise,
float max_dist, vec2 pixel_ratio, float pixel_len,
vec2 max_dir, vec2 pixel_ratio, float pixel_len,
inout float visibility, inout vec3 bent_normal)
{
float phi = M_PI * ((noise.r + iter) / aoSamples);
@ -113,12 +117,11 @@ void integrate_slice(
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) */
search_horizon(t_phi, x, x_, noise.g, max_dist, pixel_ratio, pixel_len, h1, h2);
vec2 horiz = search_horizon_sweep(t_phi, x, x_, noise.g, max_dir);
/* (Slide 54) */
h1 = -fast_acos(h1);
h2 = fast_acos(h2);
float h1 = -fast_acos(horiz.x);
float h2 = fast_acos(horiz.y);
/* Projecting Normal to Plane P defined by t_phi and omega_o */
vec3 h = vec3(t_phi.y, -t_phi.x, 0.0); /* Normal vector to Integration plane */
@ -173,6 +176,7 @@ void gtao(vec3 normal, vec3 position, vec2 noise, out float visibility
float pixel_len = length(pixel_size);
float homcco = ProjectionMatrix[2][3] * position.z + ProjectionMatrix[3][3];
float max_dist = aoDistance / homcco; /* Search distance */
vec2 max_dir = max_dist * vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]);
/* Integral over PI */
visibility = 0.0;
@ -183,7 +187,7 @@ void gtao(vec3 normal, vec3 position, vec2 noise, out float visibility
#endif
for (float i = 0.0; i < MAX_PHI_STEP; i++) {
if (i >= aoSamples) break;
integrate_slice(i, x, normal, x_, noise, max_dist, pixel_ratio, pixel_len, visibility, bent_normal);
integrate_slice(i, x, normal, x_, noise, max_dir, pixel_ratio, pixel_len, visibility, bent_normal);
}
/* aoSamples can be 0.0 to temporary disable the effect. */
@ -203,9 +207,7 @@ void gtao(vec3 normal, vec3 position, vec2 noise, out float visibility
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 lum = dot(albedo, vec3(0.3333));
float a = 2.0404 * lum - 0.3324;
float b = -4.7951 * lum + 0.6417;

View File

@ -111,6 +111,7 @@ vec3 project_point(mat4 m, vec3 v) {
return tmp.xyz / tmp.w;
}
float min_v2(vec2 v) { return min(v.x, v.y); }
float min_v3(vec3 v) { return min(v.x, min(v.y, v.z)); }
float max_v2(vec2 v) { return max(v.x, v.y); }
@ -125,6 +126,10 @@ float len_squared(vec3 a) { return dot(a, a); }
float inverse_distance(vec3 V) { return max( 1 / length(V), 1e-8); }
vec2 mip_ratio_interp(float mip) {
float low_mip = floor(mip);
return mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip);
}
/* ------- Fast Math ------- */
/* [Drobot2014a] Low Level Optimizations for GCN */

View File

@ -283,8 +283,7 @@ vec4 get_ssr_sample(
float mip = clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, MAX_MIP);
/* Correct UVs for mipmaping mis-alignment */
float low_mip = floor(mip);
ref_uvs *= mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip);
ref_uvs *= mip_ratio_interp(mip);
/* Slide 54 */
float bsdf = bsdf_ggx(N, L, V, roughnessSquared);

View File

@ -60,8 +60,7 @@ vec4 screen_space_refraction(vec3 viewPosition, vec3 N, vec3 V, float ior, float
float mip = clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, 9.0);
/* Correct UVs for mipmaping mis-alignment */
float low_mip = floor(mip);
hit_uvs *= mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip);
hit_uvs *= mip_ratio_interp(mip);
vec3 spec = textureLod(colorBuffer, hit_uvs, mip).xyz;
float mask = screen_border_mask(hit_uvs);