From 806dde70bf46d88903e8ca2d2924c94d578d5fb4 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 14 Oct 2026 18:02:35 +0200 Subject: [PATCH] feat(render): ambient occlusion with bent normals darkens only the light of the surroundings instead of the whole image, so the sun and headlights keep their full strength; the occlusion and the bent normal of the previous frame are reprojected into the lighting of the scene, with the motion of the camera and of every vehicle and cab taken into account and with depth checks against disocclusion, the ambient light follows how much of the sky the bent normal sees, and reflections of the environment are occluded where the cone of visible directions around the bent normal cuts off the reflected lobe, only where the occlusion is clear enough that open flat panels and windows keep their grazing reflections --- ci_shadervalidator/Globals.h | 1 + ci_shadervalidator/main.cpp | 2 + gl/glsl_common.cpp | 3 ++ gl/shader.cpp | 1 + gl/ubo.h | 5 ++- rendering/opengl33renderer.cpp | 71 +++++++++++++++++++++++++++------ rendering/opengl33renderer.h | 10 ++++- shaders/gtao_common.glsl | 17 ++++++++ shaders/light_common.glsl | 72 ++++++++++++++++++++++++++++++++++ shaders/postfx_gtao.frag | 13 ++++-- shaders/postfx_gtao_apply.frag | 13 ------ shaders/postfx_gtao_blur.frag | 39 ++++++++---------- 12 files changed, 194 insertions(+), 53 deletions(-) create mode 100644 shaders/gtao_common.glsl delete mode 100644 shaders/postfx_gtao_apply.frag 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)); }