diff --git a/ci_shadervalidator/Globals.h b/ci_shadervalidator/Globals.h index 16f85e80..2170f9c0 100644 --- a/ci_shadervalidator/Globals.h +++ b/ci_shadervalidator/Globals.h @@ -6,6 +6,7 @@ struct _global bool gfx_postfx_motionblur_enabled; bool gfx_skippipeline; bool gfx_extraeffects; + bool gfx_postfx_gtao_enabled; bool gfx_shadergamma; bool gfx_usegles; }; diff --git a/ci_shadervalidator/main.cpp b/ci_shadervalidator/main.cpp index 7a4b53aa..e32d209e 100644 --- a/ci_shadervalidator/main.cpp +++ b/ci_shadervalidator/main.cpp @@ -45,6 +45,7 @@ bool check_shader_allopts(std::string filename) Global.gfx_postfx_motionblur_enabled = true; Global.gfx_skippipeline = false; Global.gfx_extraeffects = true; + Global.gfx_postfx_gtao_enabled = true; Global.gfx_shadergamma = true; if (!check_shader(filename)) suc = false; @@ -55,6 +56,7 @@ bool check_shader_allopts(std::string filename) Global.gfx_postfx_motionblur_enabled = false; Global.gfx_skippipeline = true; Global.gfx_extraeffects = false; + Global.gfx_postfx_gtao_enabled = false; Global.gfx_shadergamma = false; if (!check_shader(filename)) suc = false; diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 26c31000..70635f52 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -11,6 +11,7 @@ void gl::glsl_common_setup() "#define MOTIONBLUR_ENABLED " + std::to_string((int)Global.gfx_postfx_motionblur_enabled) + "\n" + "#define POSTFX_ENABLED " + std::to_string((int)!Global.gfx_skippipeline) + "\n" + "#define EXTRAEFFECTS_ENABLED " + std::to_string((int)Global.gfx_extraeffects) + "\n" + + "#define GTAO_ENABLED " + std::to_string((int)(Global.gfx_postfx_gtao_enabled && !Global.gfx_skippipeline)) + "\n" + "#define MAX_LIGHTS " + std::to_string(MAX_LIGHTS) + "U\n" + "#define MAX_CASCADES " + std::to_string(MAX_CASCADES) + "U\n" + "#define MAX_PARAMS " + std::to_string(MAX_PARAMS) + "U\n" + @@ -77,6 +78,8 @@ void gl::glsl_common_setup() vec4 wiper_pos; vec4 wiper_timer_out; vec4 wiper_timer_return; + mat4 gtao_reprojection; + vec4 gtao_params; }; // Per-instance modelview matrices for GPU-instanced draws. diff --git a/gl/shader.cpp b/gl/shader.cpp index 9160a2c9..441c1d61 100644 --- a/gl/shader.cpp +++ b/gl/shader.cpp @@ -347,6 +347,7 @@ void gl::program::init() glUniform1i(glGetUniformLocation(*this, "shadowmap"), gl::SHADOW_TEX); glUniform1i(glGetUniformLocation(*this, "envmap"), gl::ENV_TEX); glUniform1i(glGetUniformLocation(*this, "headlightmap"), gl::HEADLIGHT_TEX); + glUniform1i(glGetUniformLocation(*this, "gtaomap"), gl::GTAO_TEX); GLuint index; diff --git a/gl/ubo.h b/gl/ubo.h index d5335b21..e5c67624 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -46,6 +46,7 @@ namespace gl const size_t SHADOW_TEX = MAX_TEXTURES + 0; const size_t ENV_TEX = MAX_TEXTURES + 1; const size_t HEADLIGHT_TEX = MAX_TEXTURES + 2; + const size_t GTAO_TEX = MAX_TEXTURES + 3; struct scene_ubs { @@ -58,9 +59,11 @@ namespace gl glm::vec4 wiper_pos; glm::vec4 wiper_timer_out; glm::vec4 wiper_timer_return; + glm::mat4 gtao_reprojection { 1.f }; + glm::vec4 gtao_params { 0.f }; }; - static_assert(sizeof(scene_ubs) == 400, "bad size of ubs"); + static_assert(sizeof(scene_ubs) == 480, "bad size of ubs"); const size_t MAX_PARAMS = 3; diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index 35391ab6..c4872d33 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -218,7 +218,6 @@ bool opengl33_renderer::Init(GLFWwindow *Window) { m_pfx_gtao = std::make_unique("gtao"); m_pfx_gtao_blur = std::make_unique("gtao_blur"); - m_pfx_gtao_apply = std::make_unique("gtao_apply"); } // Generate hemisphere kernel (z > 0 = toward surface normal) @@ -579,16 +578,17 @@ bool opengl33_renderer::init_viewport(viewport_config &vp) int gtao_h = std::max(1, vp.height / 2); vp.gtao_tex = std::make_unique(); - vp.gtao_tex->alloc_rendertarget(GL_R8, GL_RED, gtao_w, gtao_h, 1, 1, GL_CLAMP_TO_EDGE); + vp.gtao_tex->alloc_rendertarget(GL_RGBA16F, GL_RGBA, gtao_w, gtao_h, 1, 1, GL_CLAMP_TO_EDGE); vp.gtao_fb = std::make_unique(); vp.gtao_fb->attach(*vp.gtao_tex, GL_COLOR_ATTACHMENT0); if (!vp.gtao_fb->is_complete()) { ErrorLog("gtao framebuffer setup failed"); return false; } vp.gtao_blur_tex = std::make_unique(); - vp.gtao_blur_tex->alloc_rendertarget(GL_R8, GL_RED, gtao_w, gtao_h, 1, 1, GL_CLAMP_TO_EDGE); + vp.gtao_blur_tex->alloc_rendertarget(GL_RGBA16F, GL_RGBA, gtao_w, gtao_h, 1, 1, GL_CLAMP_TO_EDGE); vp.gtao_blur_fb = std::make_unique(); vp.gtao_blur_fb->attach(*vp.gtao_blur_tex, GL_COLOR_ATTACHMENT0); if (!vp.gtao_blur_fb->is_complete()) { ErrorLog("gtao blur framebuffer setup failed"); return false; } + vp.gtao_history = false; } if (!vp.main2_fb->is_complete()) @@ -977,6 +977,17 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) scene_ubo->update(scene_ubs); Render(&simulation::Environment); + if (Global.gfx_postfx_gtao_enabled && !Global.gfx_skippipeline && vp.gtao_blur_tex) + { + m_gtao_vp = &vp; + m_gtao_projection = scene_ubs.projection; + m_gtao_view = glm::dmat4{ glm::dmat3{ glm::mat3{ m_renderpass.pass_camera.modelview() } } } * glm::translate(glm::dmat4{ 1.0 }, -m_renderpass.pass_camera.position()); + m_gtao_models.clear(); + vp.gtao_blur_tex->bind(gl::GTAO_TEX); + scene_ubs.gtao_params = glm::vec4(vp.gtao_history ? 1.f : 0.f, Global.gfx_postfx_gtao_intensity, 0.f, 0.f); + setup_gtao_motion(nullptr); + } + if( Global.gfx_shadowmap_enabled ) setup_shadow_bind_map(); @@ -1049,6 +1060,13 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) Timer::subsystem.gfx_color.stop(); + if (m_gtao_vp != nullptr) + { + scene_ubs.gtao_params.x = 0.f; + scene_ubo->update(scene_ubs); + opengl_texture::unbind(gl::GTAO_TEX); + } + // store draw stats m_colorpass.draw_stats = m_renderpass.draw_stats; @@ -1094,17 +1112,20 @@ if (!Global.gfx_skippipeline) model_ubo->update(model_ubs); glViewport(0, 0, gtao_w, gtao_h); m_pfx_gtao->apply({ vp.main_texd.get() }, vp.gtao_fb.get()); - m_pfx_gtao_blur->apply({ vp.gtao_tex.get(), vp.main_texd.get() }, vp.gtao_blur_fb.get()); - + m_pfx_gtao_blur->apply({ vp.gtao_tex.get() }, vp.gtao_blur_fb.get()); glViewport(0, 0, vp.width, vp.height); - vp.main_fb->setup_drawing(1); - glEnable(GL_BLEND); - glBlendFunc(GL_ZERO, GL_SRC_COLOR); - m_pfx_gtao_apply->apply({ vp.gtao_blur_tex.get() }, vp.main_fb.get(), false); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - if (!blend) - glDisable(GL_BLEND); + if (blend) + glEnable(GL_BLEND); + + if (m_gtao_vp == &vp) + { + vp.gtao_projection = m_gtao_projection; + vp.gtao_view = m_gtao_view; + vp.gtao_models = std::move(m_gtao_models); + vp.gtao_history = true; + } } + m_gtao_vp = nullptr; if (Global.gfx_postfx_motionblur_enabled) { @@ -1406,6 +1427,7 @@ bool opengl33_renderer::Render_lowpoly( TDynamicObject *Dynamic, float const Squ ::glPushMatrix(); ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); ::glMultMatrixd( glm::value_ptr(Dynamic->mMatrix) ); + setup_gtao_motion( Dynamic ); } // HACK: reduce light level for vehicle interior if there's strong global lighting source if( false == Alpha ) { @@ -1454,6 +1476,7 @@ bool opengl33_renderer::Render_lowpoly( TDynamicObject *Dynamic, float const Squ } ::glPopMatrix(); + setup_gtao_motion( nullptr ); // TODO: check if this reset is needed. In theory each object should render all parts based on its own instance data anyway? if( Dynamic->btnOn ) { @@ -1489,6 +1512,24 @@ bool opengl33_renderer::Render_coupler_adapter( TDynamicObject *Dynamic, float c } // creates dynamic environment cubemap +void opengl33_renderer::setup_gtao_motion(TDynamicObject const *Dynamic) +{ + if (m_gtao_vp == nullptr || m_renderpass.draw_mode != rendermode::color) + return; + + glm::dmat4 motion{ 1.0 }; + if (Dynamic != nullptr) + { + glm::dmat4 const model{ glm::translate(glm::dmat4{ 1.0 }, Dynamic->GetPosition()) * Dynamic->mMatrix }; + m_gtao_models[Dynamic] = model; + auto const previous{ m_gtao_vp->gtao_models.find(Dynamic) }; + if (previous != m_gtao_vp->gtao_models.end()) + motion = previous->second * glm::inverse(model); + } + scene_ubs.gtao_reprojection = m_gtao_vp->gtao_projection * glm::mat4{ m_gtao_vp->gtao_view * motion * glm::inverse(m_gtao_view) }; + scene_ubo->update(reinterpret_cast(&scene_ubs.gtao_reprojection), static_cast(offsetof(gl::scene_ubs, gtao_reprojection)), sizeof(glm::mat4) + sizeof(glm::vec4)); +} + bool opengl33_renderer::Render_reflections(viewport_config &vp) { if( Global.reflectiontune.update_interval == 0 ) { return false; } @@ -3859,6 +3900,7 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) ::glPushMatrix(); ::glTranslated(originoffset.x, originoffset.y, originoffset.z); ::glMultMatrixd(glm::value_ptr(Dynamic->mMatrix)); + setup_gtao_motion(Dynamic); switch (m_renderpass.draw_mode) { @@ -3944,6 +3986,7 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) } ::glPopMatrix(); + setup_gtao_motion(nullptr); model_ubs.future = future_stack; @@ -3983,6 +4026,7 @@ bool opengl33_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li auto const originoffset = Dynamic->GetPosition() - m_renderpass.pass_camera.position(); ::glTranslated(originoffset.x, originoffset.y, originoffset.z); ::glMultMatrixd(glm::value_ptr(Dynamic->mMatrix)); + setup_gtao_motion(Dynamic); switch (m_renderpass.draw_mode) { @@ -4046,6 +4090,7 @@ bool opengl33_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li } // post-render restore ::glPopMatrix(); + setup_gtao_motion(nullptr); } return true; @@ -5008,6 +5053,7 @@ bool opengl33_renderer::Render_Alpha(TDynamicObject *Dynamic) ::glTranslated(originoffset.x, originoffset.y, originoffset.z); ::glMultMatrixd(glm::value_ptr(Dynamic->mMatrix)); + setup_gtao_motion(Dynamic); if (Dynamic->fShade > 0.0f) { @@ -5044,6 +5090,7 @@ bool opengl33_renderer::Render_Alpha(TDynamicObject *Dynamic) } ::glPopMatrix(); + setup_gtao_motion(nullptr); model_ubs.future = future_stack; diff --git a/rendering/opengl33renderer.h b/rendering/opengl33renderer.h index c8aa3b4f..9685c109 100644 --- a/rendering/opengl33renderer.h +++ b/rendering/opengl33renderer.h @@ -255,6 +255,10 @@ class opengl33_renderer : public gfx_renderer { std::unique_ptr gtao_blur_tex; std::unique_ptr gtao_fb; std::unique_ptr gtao_blur_fb; + bool gtao_history = false; + glm::mat4 gtao_projection; + glm::dmat4 gtao_view; + std::unordered_map gtao_models; // LDR backbuffer for offscreen rendering std::unique_ptr backbuffer_fb; @@ -283,6 +287,7 @@ class opengl33_renderer : public gfx_renderer { void Render_pass(viewport_config &vp, rendermode const Mode); // creates dynamic environment cubemap bool Render_reflections(viewport_config &vp); + void setup_gtao_motion(TDynamicObject const *Dynamic); bool Render(world_environment *Environment); void Render(scene::basic_region *Region); void Render(section_sequence::iterator First, section_sequence::iterator Last); @@ -520,7 +525,10 @@ class opengl33_renderer : public gfx_renderer { std::unique_ptr m_pfx_gtao; std::unique_ptr m_pfx_gtao_blur; - std::unique_ptr m_pfx_gtao_apply; + viewport_config *m_gtao_vp = nullptr; + glm::mat4 m_gtao_projection; + glm::dmat4 m_gtao_view; + std::unordered_map m_gtao_models; std::unique_ptr m_shadow_shader; std::unique_ptr m_alpha_shadow_shader; diff --git a/shaders/gtao_common.glsl b/shaders/gtao_common.glsl new file mode 100644 index 00000000..d2dbd9f3 --- /dev/null +++ b/shaders/gtao_common.glsl @@ -0,0 +1,17 @@ +vec2 octahedral_encode(vec3 n) +{ + n /= abs(n.x) + abs(n.y) + abs(n.z); + vec2 o = n.xy; + if (n.z < 0.0) + o = (1.0 - abs(n.yx)) * vec2(n.x >= 0.0 ? 1.0 : -1.0, n.y >= 0.0 ? 1.0 : -1.0); + return o; +} + +vec3 octahedral_decode(vec2 o) +{ + vec3 n = vec3(o, 1.0 - abs(o.x) - abs(o.y)); + float t = clamp(-n.z, 0.0, 1.0); + n.x += (n.x >= 0.0 ? -t : t); + n.y += (n.y >= 0.0 ? -t : t); + return normalize(n); +} diff --git a/shaders/light_common.glsl b/shaders/light_common.glsl index 6549fbdf..72a4cc29 100644 --- a/shaders/light_common.glsl +++ b/shaders/light_common.glsl @@ -4,6 +4,68 @@ uniform sampler2DArrayShadow shadowmap; #endif uniform sampler2D headlightmap; +#if GTAO_ENABLED +uniform sampler2D gtaomap; + +#include + +vec4 gtao_sample() +{ + vec4 none = vec4(1.0, 0.0, 0.0, 0.0); + if (gtao_params.x < 0.5) + return none; + vec4 clip = gtao_reprojection * vec4(f_pos.xyz, 1.0); + if (clip.w <= 0.0) + return none; + vec2 uv = clip.xy / clip.w * 0.5 + 0.5; + if (any(lessThan(uv, vec2(0.0))) || any(greaterThan(uv, vec2(1.0)))) + return none; + ivec2 size = textureSize(gtaomap, 0); + vec2 position = uv * vec2(size) - 0.5; + ivec2 base = ivec2(floor(position)); + vec2 f = position - vec2(base); + float visibility = 0.0; + vec3 bent = vec3(0.0); + float weights = 0.0; + for (int i = 0; i < 4; ++i) + { + ivec2 offset = ivec2(i & 1, i >> 1); + vec4 tap = texelFetch(gtaomap, clamp(base + offset, ivec2(0), size - 1), 0); + float difference = abs(tap.g - clip.w) / (clip.w * 0.08 + 0.05); + float weight = (offset.x == 1 ? f.x : 1.0 - f.x) * (offset.y == 1 ? f.y : 1.0 - f.y) * max(1.0 - difference, 0.0); + visibility += tap.r * weight; + bent += octahedral_decode(tap.ba) * weight; + weights += weight; + } + if (weights < 1e-3 || dot(bent, bent) < 1e-8) + return none; + return vec4(clamp(visibility / weights, 0.0, 1.0), normalize(bent)); +} + +float gtao_cone_overlap(float cos1, float cos2, float cosd) +{ + float r1 = acos(cos1); + float r2 = acos(cos2); + float d = acos(clamp(cosd, -1.0, 1.0)); + float smaller = 1.0 - max(cos1, cos2); + if (min(r1, r2) <= max(r1, r2) - d) + return smaller; + if (r1 + r2 <= d) + return 0.0; + float delta = abs(r1 - r2); + return smaller * smoothstep(0.0, 1.0, 1.0 - clamp((d - delta) / max(r1 + r2 - delta, 1e-4), 0.0, 1.0)); +} + +float gtao_specular(vec4 gtao, vec3 normal, vec3 reflected, float roughness) +{ + vec3 bent = (dot(gtao.yzw, gtao.yzw) > 0.5 ? gtao.yzw : normal); + float coslobe = clamp(exp2(-3.32193 * roughness * roughness), 0.0, 0.999); + float occluded = gtao_cone_overlap(sqrt(clamp(1.0 - gtao.x, 0.0, 1.0)), coslobe, dot(bent, reflected)); + float open = gtao_cone_overlap(0.0, coslobe, dot(normal, reflected)); + return mix(1.0, clamp(occluded / max(open, 1e-5), 0.0, 1.0), smoothstep(0.9, 0.5, gtao.x)); +} +#endif + #include #include @@ -298,6 +360,13 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl fragcolor *= 1.0 - parallax_occlusion; #endif +#if GTAO_ENABLED + vec4 gtao = gtao_sample(); + vec3 gtao_normal = mat3(inv_view) * fragnormal; + vec3 gtao_bent = (dot(gtao.yzw, gtao.yzw) > 0.5 ? gtao.yzw : gtao_normal); + fragcolor *= pow(gtao.x, gtao_params.y) * min((0.75 + 0.25 * gtao_bent.y) / (0.75 + 0.25 * gtao_normal.y), 1.0); +#endif + vec3 emissioncolor = vec3(emission); vec3 view_dir = normalize(-f_pos.xyz); @@ -314,6 +383,9 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl // mirroring the sky. `env_spec` is the colour to multiply the cubemap by. vec3 env_spec = EnvBRDFApprox(F0, env_roughness, NdotV); float env_spec_w = max(env_spec.r, max(env_spec.g, env_spec.b)); +#if GTAO_ENABLED + env_spec *= gtao_specular(gtao, gtao_normal, normalize(mat3(inv_view) * reflect(f_pos.xyz, fragnormal)), env_roughness); +#endif // Tint texture toward fully-saturated under strong env, weighted by the // env BRDF (so a rough/matte surface no longer gets washed toward env hue) diff --git a/shaders/postfx_gtao.frag b/shaders/postfx_gtao.frag index f006c5d9..a2a7efd4 100644 --- a/shaders/postfx_gtao.frag +++ b/shaders/postfx_gtao.frag @@ -7,6 +7,8 @@ uniform sampler2D depth_tex; #include +#include + const float PI = 3.14159265; const float HALF_PI = 1.57079633; @@ -28,7 +30,7 @@ void main() vec2 size = vec2(textureSize(depth_tex, 0)); ivec2 center = ivec2(f_coords * size); if (texelFetch(depth_tex, center, 0).r <= 0.0) { - out_color = vec4(1.0); + out_color = vec4(1.0, 60000.0, 0.0, 0.0); return; } @@ -45,7 +47,7 @@ void main() float radius = param[0].x; float pixels = radius * projection[1][1] * 0.5 * size.y / -P.z; if (pixels < 2.0) { - out_color = vec4(1.0); + out_color = vec4(1.0, -P.z, octahedral_encode(mat3(inv_view) * N)); return; } pixels = min(pixels, size.y * 0.25); @@ -59,6 +61,7 @@ void main() float jitter = fract(noise * 1.61803399); float visibility = 0.0; + vec3 bent = vec3(0.0); for (int d = 0; d < directions; ++d) { float phi = (float(d) + noise) * PI / float(directions); @@ -95,8 +98,12 @@ void main() float arc0 = (cosn + 2.0 * h0 * sin(n) - cos(2.0 * h0 - n)) * 0.25; float arc1 = (cosn + 2.0 * h1 * sin(n) - cos(2.0 * h1 - n)) * 0.25; visibility += projectedlength * (arc0 + arc1); + float middle = 0.5 * (h0 + h1); + bent += (V * cos(middle) + normalize(ortho) * sin(middle)) * projectedlength * (arc0 + arc1); } visibility = clamp(visibility / float(directions), 0.0, 1.0); float fade = smoothstep(param[1].y * 0.75, param[1].y, -P.z); - out_color = vec4(mix(visibility, 1.0, fade)); + bent = (dot(bent, bent) > 1e-8 ? normalize(bent) : N); + bent = normalize(mix(bent, N, fade)); + out_color = vec4(mix(visibility, 1.0, fade), -P.z, octahedral_encode(mat3(inv_view) * bent)); } diff --git a/shaders/postfx_gtao_apply.frag b/shaders/postfx_gtao_apply.frag deleted file mode 100644 index c8fa37a9..00000000 --- a/shaders/postfx_gtao_apply.frag +++ /dev/null @@ -1,13 +0,0 @@ -in vec2 f_coords; - -layout(location = 0) out vec4 out_color; - -#texture (ao_tex, 0, R) -uniform sampler2D ao_tex; - -#include - -void main() -{ - out_color = vec4(vec3(pow(clamp(texture(ao_tex, f_coords).r, 0.0, 1.0), param[0].y)), 1.0); -} diff --git a/shaders/postfx_gtao_blur.frag b/shaders/postfx_gtao_blur.frag index ba3b65c7..294264fb 100644 --- a/shaders/postfx_gtao_blur.frag +++ b/shaders/postfx_gtao_blur.frag @@ -2,44 +2,37 @@ in vec2 f_coords; layout(location = 0) out vec4 out_color; -#texture (ao_tex, 0, R) +#texture (ao_tex, 0, RGBA) uniform sampler2D ao_tex; -#texture (depth_tex, 1, R) -uniform sampler2D depth_tex; - #include - -float view_distance(vec2 uv) -{ - ivec2 size = textureSize(depth_tex, 0); - float depth = texelFetch(depth_tex, clamp(ivec2(uv * vec2(size)), ivec2(0), size - 1), 0).r; - if (depth <= 0.0) - return 1e6; - float ndcz = (param[1].x > 0.5 ? depth : depth * 2.0 - 1.0); - return projection[3][2] / (ndcz + projection[2][2]); -} +#include void main() { - vec2 texel = 1.0 / vec2(textureSize(ao_tex, 0)); - float center = view_distance(f_coords); - if (center >= 1e6) { - out_color = vec4(1.0); + ivec2 size = textureSize(ao_tex, 0); + ivec2 center = ivec2(f_coords * vec2(size)); + vec4 centersample = texelFetch(ao_tex, center, 0); + float centerdistance = centersample.g; + if (centerdistance >= 60000.0) { + out_color = centersample; return; } - float sum = 0.0; + float visibility = 0.0; + vec3 bent = vec3(0.0); float weights = 0.0; for (int y = -2; y <= 2; ++y) { for (int x = -2; x <= 2; ++x) { - vec2 uv = f_coords + vec2(float(x), float(y)) * texel; - float difference = (view_distance(uv) - center) / (center * 0.02 + 0.01); + vec4 tap = texelFetch(ao_tex, clamp(center + ivec2(x, y), ivec2(0), size - 1), 0); + float difference = (tap.g - centerdistance) / (centerdistance * 0.02 + 0.01); float weight = exp(-float(x * x + y * y) / 4.5 - difference * difference); - sum += texture(ao_tex, uv).r * weight; + visibility += tap.r * weight; + bent += octahedral_decode(tap.ba) * weight; weights += weight; } } - out_color = vec4(sum / max(weights, 1e-4)); + bent = (dot(bent, bent) > 1e-8 ? normalize(bent) : octahedral_decode(centersample.ba)); + out_color = vec4(visibility / max(weights, 1e-4), centerdistance, octahedral_encode(bent)); }