瀏覽代碼

Implement FXAA 3.11

mrjustaguy 1 年之前
父節點
當前提交
bc746f2962
共有 2 個文件被更改,包括 375 次插入55 次删除
  1. 15 9
      COPYRIGHT.txt
  2. 360 46
      servers/rendering/renderer_rd/shaders/effects/tonemap.glsl

+ 15 - 9
COPYRIGHT.txt

@@ -145,15 +145,21 @@ Copyright: 2001, Robert Penner
  2007-2014, Juan Linietsky, Ariel Manzur
  2007-2014, Juan Linietsky, Ariel Manzur
 License: Expat
 License: Expat
 
 
-Files: servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl
- servers/rendering/renderer_rd/shaders/ssao_blur.glsl
- servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
- servers/rendering/renderer_rd/shaders/ssao_interleave.glsl
- servers/rendering/renderer_rd/shaders/ssao.glsl
- servers/rendering/renderer_rd/shaders/ssil_blur.glsl
- servers/rendering/renderer_rd/shaders/ssil_importance_map.glsl
- servers/rendering/renderer_rd/shaders/ssil_interleave.glsl
- servers/rendering/renderer_rd/shaders/ssil.glsl
+Files: ./servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+Comment: NVidia's FXAA 3.11, simplified by Simon Rodriguez
+Copyright: 2014-2015, NVIDIA CORPORATION
+ 2017 Simon Rodriguez
+License: BSD-3-clause and Expat
+
+Files: ./servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl
+ ./servers/rendering/renderer_rd/shaders/ssao_blur.glsl
+ ./servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
+ ./servers/rendering/renderer_rd/shaders/ssao_interleave.glsl
+ ./servers/rendering/renderer_rd/shaders/ssao.glsl
+ ./servers/rendering/renderer_rd/shaders/ssil_blur.glsl
+ ./servers/rendering/renderer_rd/shaders/ssil_importance_map.glsl
+ ./servers/rendering/renderer_rd/shaders/ssil_interleave.glsl
+ ./servers/rendering/renderer_rd/shaders/ssil.glsl
 Comment: Intel ASSAO and related files
 Comment: Intel ASSAO and related files
 Copyright: 2016, Intel Corporation
 Copyright: 2016, Intel Corporation
 License: Expat
 License: Expat

+ 360 - 46
servers/rendering/renderer_rd/shaders/effects/tonemap.glsl

@@ -452,60 +452,374 @@ vec3 apply_color_correction(vec3 color) {
 #endif
 #endif
 
 
 #ifndef SUBPASS
 #ifndef SUBPASS
+
+// FXAA 3.11 compact, Ported from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/fxaa.frag
+///////////////////////////////////////////////////////////////////////////////////
+// MIT License
+//
+// Copyright (c) 2017 Simon Rodriguez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+///////////////////////////////////////////////////////////////////////////////////
+
+// Nvidia Original FXAA 3.11 License
+//----------------------------------------------------------------------------------
+// File:        es3-kepler\FXAA/FXAA3_11.h
+// SDK Version: v3.00
+// Email:       [email protected]
+// Site:        http://developer.nvidia.com/
+//
+// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//  * Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+//  * Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+//  * Neither the name of NVIDIA CORPORATION nor the names of its
+//    contributors may be used to endorse or promote products derived
+//    from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//----------------------------------------------------------------------------------
+//
+//                    NVIDIA FXAA 3.11 by TIMOTHY LOTTES
+//
+//----------------------------------------------------------------------------------
+
+float QUALITY(float q) {
+	return (q < 5 ? 1.0 : (q > 5 ? (q < 10 ? 2.0 : (q < 11 ? 4.0 : 8.0)) : 1.5));
+}
+
+float rgb2luma(vec3 rgb) {
+	return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114)));
+}
+
 vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
 vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
-	const float FXAA_REDUCE_MIN = (1.0 / 128.0);
-	const float FXAA_REDUCE_MUL = (1.0 / 8.0);
-	const float FXAA_SPAN_MAX = 8.0;
+	const float EDGE_THRESHOLD_MIN = 0.0312;
+	const float EDGE_THRESHOLD_MAX = 0.125;
+	const int ITERATIONS = 12;
+	const float SUBPIXEL_QUALITY = 0.75;
 
 
 #ifdef USE_MULTIVIEW
 #ifdef USE_MULTIVIEW
-	vec3 rgbNW = textureLod(source_color, vec3(uv_interp + vec2(-0.5, -0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
-	vec3 rgbNE = textureLod(source_color, vec3(uv_interp + vec2(0.5, -0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
-	vec3 rgbSW = textureLod(source_color, vec3(uv_interp + vec2(-0.5, 0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
-	vec3 rgbSE = textureLod(source_color, vec3(uv_interp + vec2(0.5, 0.5) * params.pixel_size, ViewIndex), 0.0).xyz * exposure * params.luminance_multiplier;
-#else
-	vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-0.5, -0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
-	vec3 rgbNE = textureLod(source_color, uv_interp + vec2(0.5, -0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
-	vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-0.5, 0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
-	vec3 rgbSE = textureLod(source_color, uv_interp + vec2(0.5, 0.5) * params.pixel_size, 0.0).xyz * exposure * params.luminance_multiplier;
-#endif
-	vec3 rgbM = color;
-	vec3 luma = vec3(0.299, 0.587, 0.114);
-	float lumaNW = dot(rgbNW, luma);
-	float lumaNE = dot(rgbNE, luma);
-	float lumaSW = dot(rgbSW, luma);
-	float lumaSE = dot(rgbSE, luma);
-	float lumaM = dot(rgbM, luma);
-	float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
-	float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
-
-	vec2 dir;
-	dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
-	dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
-
-	float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *
-					(0.25 * FXAA_REDUCE_MUL),
-			FXAA_REDUCE_MIN);
-
-	float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
-	dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
-				  max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
-						  dir * rcpDirMin)) *
-			params.pixel_size;
+	float lumaUp = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(0, 1)).xyz * exposure * params.luminance_multiplier);
+	float lumaDown = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(0, -1)).xyz * exposure * params.luminance_multiplier);
+	float lumaLeft = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(-1, 0)).xyz * exposure * params.luminance_multiplier);
+	float lumaRight = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(1, 0)).xyz * exposure * params.luminance_multiplier);
+
+	float lumaCenter = rgb2luma(color);
+
+	float lumaMin = min(lumaCenter, min(min(lumaUp, lumaDown), min(lumaLeft, lumaRight)));
+	float lumaMax = max(lumaCenter, max(max(lumaUp, lumaDown), max(lumaLeft, lumaRight)));
+
+	float lumaRange = lumaMax - lumaMin;
+
+	if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX)) {
+		return color;
+	}
+
+	float lumaDownLeft = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(-1, -1)).xyz * exposure * params.luminance_multiplier);
+	float lumaUpRight = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(1, 1)).xyz * exposure * params.luminance_multiplier);
+	float lumaUpLeft = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(-1, 1)).xyz * exposure * params.luminance_multiplier);
+	float lumaDownRight = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(1, -1)).xyz * exposure * params.luminance_multiplier);
+
+	float lumaDownUp = lumaDown + lumaUp;
+	float lumaLeftRight = lumaLeft + lumaRight;
+
+	float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
+	float lumaDownCorners = lumaDownLeft + lumaDownRight;
+	float lumaRightCorners = lumaDownRight + lumaUpRight;
+	float lumaUpCorners = lumaUpRight + lumaUpLeft;
+
+	float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
+	float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
+
+	bool isHorizontal = (edgeHorizontal >= edgeVertical);
+
+	float stepLength = isHorizontal ? params.pixel_size.y : params.pixel_size.x;
+
+	float luma1 = isHorizontal ? lumaDown : lumaLeft;
+	float luma2 = isHorizontal ? lumaUp : lumaRight;
+	float gradient1 = luma1 - lumaCenter;
+	float gradient2 = luma2 - lumaCenter;
+
+	bool is1Steepest = abs(gradient1) >= abs(gradient2);
+
+	float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
+
+	float lumaLocalAverage = 0.0;
+	if (is1Steepest) {
+		stepLength = -stepLength;
+		lumaLocalAverage = 0.5 * (luma1 + lumaCenter);
+	} else {
+		lumaLocalAverage = 0.5 * (luma2 + lumaCenter);
+	}
+
+	vec2 currentUv = uv_interp;
+	if (isHorizontal) {
+		currentUv.y += stepLength * 0.5;
+	} else {
+		currentUv.x += stepLength * 0.5;
+	}
+
+	vec2 offset = isHorizontal ? vec2(params.pixel_size.x, 0.0) : vec2(0.0, params.pixel_size.y);
+	vec3 uv1 = vec3(currentUv - offset * QUALITY(0), ViewIndex);
+	vec3 uv2 = vec3(currentUv + offset * QUALITY(0), ViewIndex);
+
+	float lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
+	float lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
+	lumaEnd1 -= lumaLocalAverage;
+	lumaEnd2 -= lumaLocalAverage;
+
+	bool reached1 = abs(lumaEnd1) >= gradientScaled;
+	bool reached2 = abs(lumaEnd2) >= gradientScaled;
+	bool reachedBoth = reached1 && reached2;
+
+	if (!reached1) {
+		uv1 -= vec3(offset * QUALITY(1), 0.0);
+	}
+	if (!reached2) {
+		uv2 += vec3(offset * QUALITY(1), 0.0);
+	}
+
+	if (!reachedBoth) {
+		for (int i = 2; i < ITERATIONS; i++) {
+			if (!reached1) {
+				lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
+				lumaEnd1 = lumaEnd1 - lumaLocalAverage;
+			}
+			if (!reached2) {
+				lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
+				lumaEnd2 = lumaEnd2 - lumaLocalAverage;
+			}
+			reached1 = abs(lumaEnd1) >= gradientScaled;
+			reached2 = abs(lumaEnd2) >= gradientScaled;
+			reachedBoth = reached1 && reached2;
+			if (!reached1) {
+				uv1 -= vec3(offset * QUALITY(i), 0.0);
+			}
+			if (!reached2) {
+				uv2 += vec3(offset * QUALITY(i), 0.0);
+			}
+			if (reachedBoth) {
+				break;
+			}
+		}
+	}
+
+	float distance1 = isHorizontal ? (uv_interp.x - uv1.x) : (uv_interp.y - uv1.y);
+	float distance2 = isHorizontal ? (uv2.x - uv_interp.x) : (uv2.y - uv_interp.y);
+
+	bool isDirection1 = distance1 < distance2;
+	float distanceFinal = min(distance1, distance2);
+
+	float edgeThickness = (distance1 + distance2);
+
+	bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
+
+	bool correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller;
+	bool correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller;
+
+	bool correctVariation = isDirection1 ? correctVariation1 : correctVariation2;
+
+	float pixelOffset = -distanceFinal / edgeThickness + 0.5;
+
+	float finalOffset = correctVariation ? pixelOffset : 0.0;
+
+	float lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
+
+	float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
+	float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
+
+	float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY;
+
+	finalOffset = max(finalOffset, subPixelOffsetFinal);
+
+	vec3 finalUv = vec3(uv_interp, ViewIndex);
+	if (isHorizontal) {
+		finalUv.y += finalOffset * stepLength;
+	} else {
+		finalUv.x += finalOffset * stepLength;
+	}
+
+	vec3 finalColor = textureLod(source_color, finalUv, 0.0).xyz * exposure * params.luminance_multiplier;
+	return finalColor;
 
 
-#ifdef USE_MULTIVIEW
-	vec3 rgbA = 0.5 * exposure * (textureLod(source_color, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz) * params.luminance_multiplier;
-	vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz) * params.luminance_multiplier;
 #else
 #else
-	vec3 rgbA = 0.5 * exposure * (textureLod(source_color, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source_color, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz) * params.luminance_multiplier;
-	vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source_color, uv_interp + dir * 0.5, 0.0).xyz) * params.luminance_multiplier;
-#endif
+	float lumaUp = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(0, 1)).xyz * exposure * params.luminance_multiplier);
+	float lumaDown = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(0, -1)).xyz * exposure * params.luminance_multiplier);
+	float lumaLeft = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(-1, 0)).xyz * exposure * params.luminance_multiplier);
+	float lumaRight = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(1, 0)).xyz * exposure * params.luminance_multiplier);
+
+	float lumaCenter = rgb2luma(color);
+
+	float lumaMin = min(lumaCenter, min(min(lumaUp, lumaDown), min(lumaLeft, lumaRight)));
+	float lumaMax = max(lumaCenter, max(max(lumaUp, lumaDown), max(lumaLeft, lumaRight)));
+
+	float lumaRange = lumaMax - lumaMin;
+
+	if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX)) {
+		return color;
+	}
+
+	float lumaDownLeft = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(-1, -1)).xyz * exposure * params.luminance_multiplier);
+	float lumaUpRight = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(1, 1)).xyz * exposure * params.luminance_multiplier);
+	float lumaUpLeft = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(-1, 1)).xyz * exposure * params.luminance_multiplier);
+	float lumaDownRight = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(1, -1)).xyz * exposure * params.luminance_multiplier);
+
+	float lumaDownUp = lumaDown + lumaUp;
+	float lumaLeftRight = lumaLeft + lumaRight;
+
+	float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
+	float lumaDownCorners = lumaDownLeft + lumaDownRight;
+	float lumaRightCorners = lumaDownRight + lumaUpRight;
+	float lumaUpCorners = lumaUpRight + lumaUpLeft;
+
+	float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
+	float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
+
+	bool isHorizontal = (edgeHorizontal >= edgeVertical);
+
+	float stepLength = isHorizontal ? params.pixel_size.y : params.pixel_size.x;
+
+	float luma1 = isHorizontal ? lumaDown : lumaLeft;
+	float luma2 = isHorizontal ? lumaUp : lumaRight;
+	float gradient1 = luma1 - lumaCenter;
+	float gradient2 = luma2 - lumaCenter;
+
+	bool is1Steepest = abs(gradient1) >= abs(gradient2);
+
+	float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
+
+	float lumaLocalAverage = 0.0;
+	if (is1Steepest) {
+		stepLength = -stepLength;
+		lumaLocalAverage = 0.5 * (luma1 + lumaCenter);
+	} else {
+		lumaLocalAverage = 0.5 * (luma2 + lumaCenter);
+	}
+
+	vec2 currentUv = uv_interp;
+	if (isHorizontal) {
+		currentUv.y += stepLength * 0.5;
+	} else {
+		currentUv.x += stepLength * 0.5;
+	}
+
+	vec2 offset = isHorizontal ? vec2(params.pixel_size.x, 0.0) : vec2(0.0, params.pixel_size.y);
+	vec2 uv1 = currentUv - offset * QUALITY(0);
+	vec2 uv2 = currentUv + offset * QUALITY(0);
+
+	float lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
+	float lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
+	lumaEnd1 -= lumaLocalAverage;
+	lumaEnd2 -= lumaLocalAverage;
+
+	bool reached1 = abs(lumaEnd1) >= gradientScaled;
+	bool reached2 = abs(lumaEnd2) >= gradientScaled;
+	bool reachedBoth = reached1 && reached2;
+
+	if (!reached1) {
+		uv1 -= offset * QUALITY(1);
+	}
+	if (!reached2) {
+		uv2 += offset * QUALITY(1);
+	}
+
+	if (!reachedBoth) {
+		for (int i = 2; i < ITERATIONS; i++) {
+			if (!reached1) {
+				lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
+				lumaEnd1 = lumaEnd1 - lumaLocalAverage;
+			}
+			if (!reached2) {
+				lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
+				lumaEnd2 = lumaEnd2 - lumaLocalAverage;
+			}
+			reached1 = abs(lumaEnd1) >= gradientScaled;
+			reached2 = abs(lumaEnd2) >= gradientScaled;
+			reachedBoth = reached1 && reached2;
+			if (!reached1) {
+				uv1 -= offset * QUALITY(i);
+			}
+			if (!reached2) {
+				uv2 += offset * QUALITY(i);
+			}
+			if (reachedBoth) {
+				break;
+			}
+		}
+	}
+
+	float distance1 = isHorizontal ? (uv_interp.x - uv1.x) : (uv_interp.y - uv1.y);
+	float distance2 = isHorizontal ? (uv2.x - uv_interp.x) : (uv2.y - uv_interp.y);
+
+	bool isDirection1 = distance1 < distance2;
+	float distanceFinal = min(distance1, distance2);
+
+	float edgeThickness = (distance1 + distance2);
 
 
-	float lumaB = dot(rgbB, luma);
-	if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
-		return rgbA;
+	bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
+
+	bool correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller;
+	bool correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller;
+
+	bool correctVariation = isDirection1 ? correctVariation1 : correctVariation2;
+
+	float pixelOffset = -distanceFinal / edgeThickness + 0.5;
+
+	float finalOffset = correctVariation ? pixelOffset : 0.0;
+
+	float lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
+
+	float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
+	float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
+
+	float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY;
+
+	finalOffset = max(finalOffset, subPixelOffsetFinal);
+
+	vec2 finalUv = uv_interp;
+	if (isHorizontal) {
+		finalUv.y += finalOffset * stepLength;
 	} else {
 	} else {
-		return rgbB;
+		finalUv.x += finalOffset * stepLength;
 	}
 	}
+
+	vec3 finalColor = textureLod(source_color, finalUv, 0.0).xyz * exposure * params.luminance_multiplier;
+	return finalColor;
+
+#endif
 }
 }
 #endif // !SUBPASS
 #endif // !SUBPASS