Eevee: Optimize and improve GTAO Horizon search
authorClément Foucault <foucault.clem@gmail.com>
Sun, 13 Aug 2017 12:30:24 +0000 (14:30 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Sun, 13 Aug 2017 12:30:47 +0000 (14:30 +0200)
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.

source/blender/draw/engines/eevee/eevee_materials.c
source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
source/blender/draw/engines/eevee/shaders/ssr_lib.glsl

index 517d7780719e53a9dfe4d4f04dece5eeb006fec4..e8bf461e91f6dcf003002fc83ab4dd3d5d82de32 100644 (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) {
index 10758f74bf26bb51543b0b5995e6fe6ba687e92c..6dc02d26321c68a93f18952d5a641bb9befa79a1 100644 (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;
 
-       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);
+       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;
 
-       return mix(h, max_h, blend);
-}
+       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;
 
-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)
-{
-       ofs += ofs; /* Step size is doubled each iteration */
+       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;
 
-       vec2 s_ = t_phi * ofs * rand * pixel_ratio; /* s^ Screen coordinate */
-       vec2 co;
+       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;
 
-       co = x_ + s_;
-       h1 = get_max_horizon(co, x, h1, j);
-
-       co = x_ - s_;
-       h2 = get_max_horizon(co, x, h2, j);
+       /* Blend weight after half the aoDistance to fade artifacts */
+       vec4 blend = saturate((1.0 - len / aoDistance) * 2.0);
 
-       j += 0.5;
+       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_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)
+#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)
 {
-       float ofs = 1.5 * pixel_len;
-       float j = 0.0;
-
-#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;
-       }
-#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;
+       max_dir *= max_v2(abs(t_phi));
 
-       search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
-       if (ofs > max_dist)     return;
+       /* Convert to pixel space. */
+       t_phi /= vec2(textureSize(maxzBuffer, 0));
 
-       search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
-       if (ofs > max_dist)     return;
+       /* Avoid division by 0 */
+       t_phi += vec2(1e-5);
 
-       search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
-       if (ofs > max_dist)     return;
+       jitter *= 0.25;
 
-       search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
-       if (ofs > max_dist)     return;
+       /* 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;
 
-       search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
-       if (ofs > max_dist)     return;
+       vec2 min_iter = max(-iter1, -iter2);
+       vec2 max_iter = max( iter1,  iter2);
 
-       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));
 
-       search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
-       if (ofs > max_dist)     return;
+       vec2 h = vec2(-1.0); /* init at cos(pi) */
 
-       search_step(t_phi, x, x_, rand, pixel_ratio, j, ofs, h1, h2);
-       if (ofs > max_dist)     return;
+       /* 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);
+       }
 
-       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);
+       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;
index 4d2f92c695514af31e498e8be81d14643f833f8e..ee2bc08a38a2b805e4c0ed7ee54e5a58a0b9bcdc 100644 (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 */
index 267c52f05089c36d4a7a9a330812fe34c23edd7d..7dc047882c392ab8223306e3ee9a045681344d75 100644 (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);
index 432c2ccf8ddc6be22380af6ac064e271dfc56d78..68e0c2a682fec703600baa1474422c33fa0af6a4 100644 (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);