From a4da0992bbccba2ecabc49310f0ad40b52c568a5 Mon Sep 17 00:00:00 2001 From: Jonatas Esteves Date: Wed, 1 Sep 2021 06:43:03 -0300 Subject: [PATCH] New port of SMAA Adds a new port of SMAA. This includes the original sources unmodified. The original does a great job of ensuring compatibility with any shader compiler, so no need to modify it. - Add a runtime toggle to choose edge detection technique; - Position independent, doesn't need to be the first shader in a chain anymore. --- anti-aliasing/fxaa.slangp | 2 + anti-aliasing/shaders/smaa/AreaTex.png | Bin 18698 -> 30346 bytes anti-aliasing/shaders/smaa/SMAA.frag | 710 +++++++++ anti-aliasing/shaders/smaa/SMAA.hlsl | 1361 +++++++++++++++++ anti-aliasing/shaders/smaa/SearchTex.png | Bin 122 -> 126 bytes .../smaa/smaa-blend-weight-calculation.slang | 469 ------ anti-aliasing/shaders/smaa/smaa-common.h | 631 -------- .../shaders/smaa/smaa-edge-detection.slang | 185 --- .../smaa/smaa-neighborhood-blending.slang | 102 -- anti-aliasing/shaders/smaa/smaa-pass0.slang | 54 + anti-aliasing/shaders/smaa/smaa-pass1.slang | 49 + anti-aliasing/shaders/smaa/smaa-pass2.slang | 45 + anti-aliasing/smaa+linear.slangp | 32 + anti-aliasing/smaa+sharpen.slangp | 37 + anti-aliasing/smaa.slangp | 23 +- 15 files changed, 2307 insertions(+), 1393 deletions(-) create mode 100644 anti-aliasing/shaders/smaa/SMAA.frag create mode 100644 anti-aliasing/shaders/smaa/SMAA.hlsl delete mode 100644 anti-aliasing/shaders/smaa/smaa-blend-weight-calculation.slang delete mode 100644 anti-aliasing/shaders/smaa/smaa-common.h delete mode 100644 anti-aliasing/shaders/smaa/smaa-edge-detection.slang delete mode 100644 anti-aliasing/shaders/smaa/smaa-neighborhood-blending.slang create mode 100644 anti-aliasing/shaders/smaa/smaa-pass0.slang create mode 100644 anti-aliasing/shaders/smaa/smaa-pass1.slang create mode 100644 anti-aliasing/shaders/smaa/smaa-pass2.slang create mode 100644 anti-aliasing/smaa+linear.slangp create mode 100644 anti-aliasing/smaa+sharpen.slangp diff --git a/anti-aliasing/fxaa.slangp b/anti-aliasing/fxaa.slangp index f5dd880..7de8fd3 100644 --- a/anti-aliasing/fxaa.slangp +++ b/anti-aliasing/fxaa.slangp @@ -2,3 +2,5 @@ shaders = 1 shader0 = shaders/fxaa.slang filter_linear0 = true +scale_type0 = source +scale0 = 1.0 diff --git a/anti-aliasing/shaders/smaa/AreaTex.png b/anti-aliasing/shaders/smaa/AreaTex.png index 50ab6f1e54d1871da7018f93bbe5077ce2915857..d0184f77e7834b5ab8a7fabb42efce57c4c9ff12 100644 GIT binary patch literal 30346 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

qT*v^jGmL5LFF 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 literal 18698 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 G3gb^CihOkd diff --git a/anti-aliasing/shaders/smaa/SMAA.frag b/anti-aliasing/shaders/smaa/SMAA.frag new file mode 100644 index 0000000..d4f1b1a --- /dev/null +++ b/anti-aliasing/shaders/smaa/SMAA.frag @@ -0,0 +1,710 @@ +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * 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. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions 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. + */ + + +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- diff --git a/anti-aliasing/shaders/smaa/SMAA.hlsl b/anti-aliasing/shaders/smaa/SMAA.hlsl new file mode 100644 index 0000000..2c8d497 --- /dev/null +++ b/anti-aliasing/shaders/smaa/SMAA.hlsl @@ -0,0 +1,1361 @@ +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * 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. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions 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. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------· + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------· + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS diff --git a/anti-aliasing/shaders/smaa/SearchTex.png b/anti-aliasing/shaders/smaa/SearchTex.png index e21b285b4e94a40d16a2fb7cd9e338b219a8676c..8d3dd964c9f0de3ea9d029c26e9be812a9b2dc62 100644 GIT binary patch delta 90 zcmb=bo1hu`kAb0{0R+^Q{q6uM15X#nkcwN$56%R{Zq14cS#o>Ticp0EM;iFT7BaFK pZAsv8Nn6%*mg8_P+ta8M0ziG+OWXf$H$DL}!_(EzWt~$(69BasAvgd4 delta 86 zcmb=cnxGlTz`#&nU;pplzqplmJ^?v8o-U3d5|@)BUR*hKKtV`g#vy?N3``PCGa5FC q9a6Y(QFM)Jiz0(_LyhhfW(FB05mv6V9It@-7(8A5T-G@yGywq3s2>sl diff --git a/anti-aliasing/shaders/smaa/smaa-blend-weight-calculation.slang b/anti-aliasing/shaders/smaa/smaa-blend-weight-calculation.slang deleted file mode 100644 index 7316ee9..0000000 --- a/anti-aliasing/shaders/smaa/smaa-blend-weight-calculation.slang +++ /dev/null @@ -1,469 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; -} params; - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; -} global; - -#include "smaa-common.h" - -#pragma stage vertex -layout(location = 0) in vec4 Position; -layout(location = 1) in vec2 TexCoord; -layout(location = 0) out vec2 texcoord; -layout(location = 1) out vec2 pixcoord; -layout(location = 2) out vec4 offset[3]; - -void main() -{ - gl_Position = global.MVP * Position; - texcoord = TexCoord; - - pixcoord = texcoord * SMAA_RT_METRICS.zw; - - // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): - offset[0] = fma(SMAA_RT_METRICS.xyxy, vec4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); - offset[1] = fma(SMAA_RT_METRICS.xyxy, vec4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); - - // And these for the searches, they indicate the ends of the loops: - offset[2] = fma(SMAA_RT_METRICS.xxyy, - vec4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), - vec4(offset[0].xz, offset[1].yw)); -} - -#pragma stage fragment -layout(location = 0) in vec2 texcoord; -layout(location = 1) in vec2 pixcoord; -layout(location = 2) in vec4 offset[3]; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; -layout(set = 0, binding = 3) uniform sampler2D areaTex; -layout(set = 0, binding = 4) uniform sampler2D searchTex; - -//----------------------------------------------------------------------------- -// Blending Weight Calculation Pixel Shader (Second Pass) - -/** - * Allows to decode two binary values from a bilinear-filtered access. - */ -vec2 SMAADecodeDiagBilinearAccess(vec2 e) { - // Bilinear access for fetching 'e' have a 0.25 offset, and we are - // interested in the R and G edges: - // - // +---G---+-------+ - // | x o R x | - // +-------+-------+ - // - // Then, if one of these edge is enabled: - // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 - // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 - // - // This function will unpack the values (mad + mul + round): - // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 - e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); - return round(e); -} - -vec4 SMAADecodeDiagBilinearAccess(vec4 e) { - e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); - return round(e); -} - -/** - * These functions allows to perform diagonal pattern searches. - */ -vec2 SMAASearchDiag1(sampler2D edgesTex, vec2 texcoord, vec2 dir, out vec2 e) { - vec4 coord = vec4(texcoord, -1.0, 1.0); - vec3 t = vec3(SMAA_RT_METRICS.xy, 1.0); - while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && - coord.w > 0.9) { - coord.xyz = fma(t, vec3(dir, 1.0), coord.xyz); - e = textureLod(edgesTex, coord.xy, 0.0).rg; - coord.w = dot(e, vec2(0.5, 0.5)); - } - return coord.zw; -} - -vec2 SMAASearchDiag2(sampler2D edgesTex, vec2 texcoord, vec2 dir, out vec2 e) { - vec4 coord = vec4(texcoord, -1.0, 1.0); - coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization - vec3 t = vec3(SMAA_RT_METRICS.xy, 1.0); - while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && - coord.w > 0.9) { - coord.xyz = fma(t, vec3(dir, 1.0), coord.xyz); - - // @SearchDiag2Optimization - // Fetch both edges at once using bilinear filtering: - e = textureLod(edgesTex, coord.xy, 0.0).rg; - e = SMAADecodeDiagBilinearAccess(e); - - // Non-optimized version: - // e.g = textureLod(edgesTex, coord.xy, 0.0).g; - // e.r = textureLod(edgesTex, coord.xy, ivec2(1, 0)).r; - - coord.w = dot(e, vec2(0.5, 0.5)); - } - return coord.zw; -} - -/** - * Similar to SMAAArea, this calculates the area corresponding to a certain - * diagonal distance and crossing edges 'e'. - */ -vec2 SMAAAreaDiag(sampler2D areaTex, vec2 dist, vec2 e, float offset) { - vec2 texcoord = fma(vec2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); - - // We do a scale and bias for mapping to texel space: - texcoord = fma(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); - - // Diagonal areas are on the second half of the texture: - texcoord.x += 0.5; - - // Move to proper place, according to the subpixel offset: - texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; - - // Do it! - return SMAA_AREATEX_SELECT(textureLod(areaTex, texcoord, 0.0)); -} - -/** - * This searches for diagonal patterns and returns the corresponding weights. - */ -vec2 SMAACalculateDiagWeights(sampler2D edgesTex, sampler2D areaTex, vec2 texcoord, vec2 e, vec4 subsampleIndices) { - vec2 weights = vec2(0.0, 0.0); - - // Search for the line ends: - vec4 d; - vec2 end; - if (e.r > 0.0) { - d.xz = SMAASearchDiag1(edgesTex, texcoord, vec2(-1.0, 1.0), end); - d.x += float(end.y > 0.9); - } else - d.xz = vec2(0.0, 0.0); - d.yw = SMAASearchDiag1(edgesTex, texcoord, vec2(1.0, -1.0), end); - -// SMAA_BRANCH - if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 - // Fetch the crossing edges: - vec4 coords = fma(vec4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); - vec4 c; - c.xy = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(-1, 0)).rg; - c.zw = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1, 0)).rg; - c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); - - // Non-optimized version: - // vec4 coords = fma(vec4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); - // vec4 c; - // c.x = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(-1, 0)).g; - // c.y = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2( 0, 0)).r; - // c.z = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1, 0)).g; - // c.w = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1, -1)).r; - - // Merge crossing edges at each side into a single value: - vec2 cc = fma(vec2(2.0, 2.0), c.xz, c.yw); - - // Remove the crossing edge if we didn't found the end of the line: - SMAAMovc(bvec2(step(0.9, d.zw)), cc, vec2(0.0, 0.0)); - - // Fetch the areas for this line: - weights += SMAAAreaDiag(areaTex, d.xy, cc, subsampleIndices.z); - } - - // Search for the line ends: - d.xz = SMAASearchDiag2(edgesTex, texcoord, vec2(-1.0, -1.0), end); - if (textureLodOffset(edgesTex, texcoord, 0.0, ivec2(1, 0)).r > 0.0) { - d.yw = SMAASearchDiag2(edgesTex, texcoord, vec2(1.0, 1.0), end); - d.y += float(end.y > 0.9); - } else - d.yw = vec2(0.0, 0.0); - -// SMAA_BRANCH - if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 - // Fetch the crossing edges: - vec4 coords = fma(vec4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); - vec4 c; - c.x = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(-1, 0)).g; - c.y = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2( 0, -1)).r; - c.zw = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1, 0)).gr; - vec2 cc = fma(vec2(2.0, 2.0), c.xz, c.yw); - - // Remove the crossing edge if we didn't found the end of the line: - SMAAMovc(bvec2(step(0.9, d.zw)), cc, vec2(0.0, 0.0)); - - // Fetch the areas for this line: - weights += SMAAAreaDiag(areaTex, d.xy, cc, subsampleIndices.w).gr; - } - - return weights; -} - -//----------------------------------------------------------------------------- -// Horizontal/Vertical Search Functions - -/** - * This allows to determine how much length should we add in the last step - * of the searches. It takes the bilinearly interpolated edge (see - * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and - * crossing edges are active. - */ -float SMAASearchLength(sampler2D searchTex, vec2 e, float offset) { - // The texture is flipped vertically, with left and right cases taking half - // of the space horizontally: - vec2 scale = SMAA_SEARCHTEX_SIZE * vec2(0.5, -1.0); - vec2 bias = SMAA_SEARCHTEX_SIZE * vec2(offset, 1.0); - - // Scale and bias to access texel centers: - scale += vec2(-1.0, 1.0); - bias += vec2( 0.5, -0.5); - - // Convert from pixel coordinates to texcoords: - // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) - scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; - bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; - - // Lookup the search texture: - return SMAA_SEARCHTEX_SELECT(textureLod(searchTex, fma(scale, e, bias), 0.0)); -} - -/** - * Horizontal/vertical search functions for the 2nd pass. - */ -float SMAASearchXLeft(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { - /** - * @PSEUDO_GATHER4 - * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to - * sample between edge, thus fetching four edges in a row. - * Sampling with different offsets in each direction allows to disambiguate - * which edges are active from the four fetched ones. - */ - vec2 e = vec2(0.0, 1.0); - while (texcoord.x > end && - e.g > 0.8281 && // Is there some edge not activated? - e.r == 0.0) { // Or is there a crossing edge that breaks the line? - e = textureLod(edgesTex, texcoord, 0.0).rg; - texcoord = fma(-vec2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); - } - - float offset = fma(-(255.0 / 127.0), SMAASearchLength(searchTex, e, 0.0), 3.25); - return fma(SMAA_RT_METRICS.x, offset, texcoord.x); - - // Non-optimized version: - // We correct the previous (-0.25, -0.125) offset we applied: - // texcoord.x += 0.25 * SMAA_RT_METRICS.x; - - // The searches are bias by 1, so adjust the coords accordingly: - // texcoord.x += SMAA_RT_METRICS.x; - - // Disambiguate the length added by the last step: - // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step - // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(searchTex, e, 0.0); - // return fma(SMAA_RT_METRICS.x, offset, texcoord.x); -} - -float SMAASearchXRight(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { - vec2 e = vec2(0.0, 1.0); - while (texcoord.x < end && - e.g > 0.8281 && // Is there some edge not activated? - e.r == 0.0) { // Or is there a crossing edge that breaks the line? - e = textureLod(edgesTex, texcoord, 0.0).rg; - texcoord = fma(vec2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); - } - float offset = fma(-(255.0 / 127.0), SMAASearchLength(searchTex, e, 0.5), 3.25); - return fma(-SMAA_RT_METRICS.x, offset, texcoord.x); -} - -float SMAASearchYUp(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { - vec2 e = vec2(1.0, 0.0); - while (texcoord.y > end && - e.r > 0.8281 && // Is there some edge not activated? - e.g == 0.0) { // Or is there a crossing edge that breaks the line? - e = textureLod(edgesTex, texcoord, 0.0).rg; - texcoord = fma(-vec2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); - } - float offset = fma(-(255.0 / 127.0), SMAASearchLength(searchTex, e.gr, 0.0), 3.25); - return fma(SMAA_RT_METRICS.y, offset, texcoord.y); -} - -float SMAASearchYDown(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { - vec2 e = vec2(1.0, 0.0); - while (texcoord.y < end && - e.r > 0.8281 && // Is there some edge not activated? - e.g == 0.0) { // Or is there a crossing edge that breaks the line? - e = textureLod(edgesTex, texcoord, 0.0).rg; - texcoord = fma(vec2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); - } - float offset = fma(-(255.0 / 127.0), SMAASearchLength(searchTex, e.gr, 0.5), 3.25); - return fma(-SMAA_RT_METRICS.y, offset, texcoord.y); -} - -/** - * Ok, we have the distance and both crossing edges. So, what are the areas - * at each side of current edge? - */ -vec2 SMAAArea(sampler2D areaTex, vec2 dist, float e1, float e2, float offset) { - // Rounding prevents precision errors of bilinear filtering: - vec2 texcoord = fma(vec2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * vec2(e1, e2)), dist); - - // We do a scale and bias for mapping to texel space: - texcoord = fma(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); - - // Move to proper place, according to the subpixel offset: - texcoord.y = fma(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); - - // Do it! - return SMAA_AREATEX_SELECT(textureLod(areaTex, texcoord, 0.0)); -} - -//----------------------------------------------------------------------------- -// Corner Detection Functions - -void SMAADetectHorizontalCornerPattern(sampler2D edgesTex, inout vec2 weights, vec4 texcoord, vec2 d) { - #if !defined(SMAA_DISABLE_CORNER_DETECTION) - vec2 leftRight = step(d.xy, d.yx); - vec2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; - - rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. - - vec2 factor = vec2(1.0, 1.0); - factor.x -= rounding.x * textureLodOffset(edgesTex, texcoord.xy, 0.0, ivec2(0, 1)).r; - factor.x -= rounding.y * textureLodOffset(edgesTex, texcoord.zw, 0.0, ivec2(1, 1)).r; - factor.y -= rounding.x * textureLodOffset(edgesTex, texcoord.xy, 0.0, ivec2(0, -2)).r; - factor.y -= rounding.y * textureLodOffset(edgesTex, texcoord.zw, 0.0, ivec2(1, -2)).r; - - weights *= clamp(factor, 0.0, 1.0); - #endif -} - -void SMAADetectVerticalCornerPattern(sampler2D edgesTex, inout vec2 weights, vec4 texcoord, vec2 d) { - #if !defined(SMAA_DISABLE_CORNER_DETECTION) - vec2 leftRight = step(d.xy, d.yx); - vec2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; - - rounding /= leftRight.x + leftRight.y; - - vec2 factor = vec2(1.0, 1.0); - factor.x -= rounding.x * textureLodOffset(edgesTex, texcoord.xy, 0.0, ivec2( 1, 0)).g; - factor.x -= rounding.y * textureLodOffset(edgesTex, texcoord.zw, 0.0, ivec2( 1, 1)).g; - factor.y -= rounding.x * textureLodOffset(edgesTex, texcoord.xy, 0.0, ivec2(-2, 0)).g; - factor.y -= rounding.y * textureLodOffset(edgesTex, texcoord.zw, 0.0, ivec2(-2, 1)).g; - - weights *= clamp(factor, 0.0, 1.0); - #endif -} - -vec4 SMAABlendingWeightCalculationPS(vec2 texcoord, - vec2 pixcoord, - vec4 offset[3], - sampler2D edgesTex, - sampler2D areaTex, - sampler2D searchTex, - vec4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. - vec4 weights = vec4(0.0, 0.0, 0.0, 0.0); - - vec2 e = texture(edgesTex, texcoord).rg; - -// SMAA_BRANCH - if (e.g > 0.0) { // Edge at north - #if !defined(SMAA_DISABLE_DIAG_DETECTION) - // Diagonals have both north and west edges, so searching for them in - // one of the boundaries is enough. - weights.rg = SMAACalculateDiagWeights(edgesTex, areaTex, texcoord, e, subsampleIndices); - - // We give priority to diagonals, so if we find a diagonal we skip - // horizontal/vertical processing. -// SMAA_BRANCH - if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 - #endif - - vec2 d; - - // Find the distance to the left: - vec3 coords; - coords.x = SMAASearchXLeft(edgesTex, searchTex, offset[0].xy, offset[2].x); - coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) - d.x = coords.x; - - // Now fetch the left crossing edges, two at a time using bilinear - // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to - // discern what value each edge has: - float e1 = textureLod(edgesTex, coords.xy, 0.0).r; - - // Find the distance to the right: - coords.z = SMAASearchXRight(edgesTex, searchTex, offset[0].zw, offset[2].y); - d.y = coords.z; - - // We want the distances to be in pixel units (doing this here allow to - // better interleave arithmetic and memory accesses): - d = abs(round(fma(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); - - // SMAAArea below needs a sqrt, as the areas texture is compressed - // quadratically: - vec2 sqrt_d = sqrt(d); - - // Fetch the right crossing edges: - float e2 = textureLodOffset(edgesTex, coords.zy, 0.0, ivec2(1, 0)).r; - - // Ok, we know how this pattern looks like, now it is time for getting - // the actual area: - weights.rg = SMAAArea(areaTex, sqrt_d, e1, e2, subsampleIndices.y); - - // Fix corners: - coords.y = texcoord.y; - SMAADetectHorizontalCornerPattern(edgesTex, weights.rg, coords.xyzy, d); - - #if !defined(SMAA_DISABLE_DIAG_DETECTION) - } else - e.r = 0.0; // Skip vertical processing. - #endif - } - -// SMAA_BRANCH - if (e.r > 0.0) { // Edge at west - vec2 d; - - // Find the distance to the top: - vec3 coords; - coords.y = SMAASearchYUp(edgesTex, searchTex, offset[1].xy, offset[2].z); - coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; - d.x = coords.y; - - // Fetch the top crossing edges: - float e1 = textureLod(edgesTex, coords.xy, 0.0).g; - - // Find the distance to the bottom: - coords.z = SMAASearchYDown(edgesTex, searchTex, offset[1].zw, offset[2].w); - d.y = coords.z; - - // We want the distances to be in pixel units: - d = abs(round(fma(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); - - // SMAAArea below needs a sqrt, as the areas texture is compressed - // quadratically: - vec2 sqrt_d = sqrt(d); - - // Fetch the bottom crossing edges: - float e2 = textureLodOffset(edgesTex, coords.xz, 0.0, ivec2(0, 1)).g; - - // Get the area for this direction: - weights.ba = SMAAArea(areaTex, sqrt_d, e1, e2, subsampleIndices.x); - - // Fix corners: - coords.x = texcoord.x; - SMAADetectVerticalCornerPattern(edgesTex, weights.ba, coords.xyxz, d); - } - - return weights; -} - -void main() -{ - FragColor = SMAABlendingWeightCalculationPS(texcoord, pixcoord, offset, Source, areaTex, searchTex, vec4(0.0)); -} \ No newline at end of file diff --git a/anti-aliasing/shaders/smaa/smaa-common.h b/anti-aliasing/shaders/smaa/smaa-common.h deleted file mode 100644 index 96e7a35..0000000 --- a/anti-aliasing/shaders/smaa/smaa-common.h +++ /dev/null @@ -1,631 +0,0 @@ -/** - * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) - * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) - * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) - * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) - * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * 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. As clarification, there - * is no requirement that the copyright notice and permission be included in - * binary distributions 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. - */ - - #define SMAA_GLSL_4 -#define SMAA_PRESET_HIGH -//#include "SMAA.h" -#define SMAA_RT_METRICS vec4(params.SourceSize.z, params.SourceSize.w, params.SourceSize.x, params.SourceSize.y) - -/** - * _______ ___ ___ ___ ___ - * / || \/ | / \ / \ - * | (---- | \ / | / ^ \ / ^ \ - * \ \ | |\/| | / /_\ \ / /_\ \ - * ----) | | | | | / _____ \ / _____ \ - * |_______/ |__| |__| /__/ \__\ /__/ \__\ - * - * E N H A N C E D - * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G - * - * http://www.iryoku.com/smaa/ - * - * Hi, welcome aboard! - * - * Here you'll find instructions to get the shader up and running as fast as - * possible. - * - * IMPORTANTE NOTICE: when updating, remember to update both this file and the - * precomputed textures! They may change from version to version. - * - * The shader has three passes, chained together as follows: - * - * |input|------------------? - * v | - * [ SMAA*EdgeDetection ] | - * v | - * |edgesTex| | - * v | - * [ SMAABlendingWeightCalculation ] | - * v | - * |blendTex| | - * v | - * [ SMAANeighborhoodBlending ] <------? - * v - * |output| - * - * Note that each [pass] has its own vertex and pixel shader. Remember to use - * oversized triangles instead of quads to avoid overshading along the - * diagonal. - * - * You've three edge detection methods to choose from: luma, color or depth. - * They represent different quality/performance and anti-aliasing/sharpness - * tradeoffs, so our recommendation is for you to choose the one that best - * suits your particular scenario: - * - * - Depth edge detection is usually the fastest but it may miss some edges. - * - * - Luma edge detection is usually more expensive than depth edge detection, - * but catches visible edges that depth edge detection can miss. - * - * - Color edge detection is usually the most expensive one but catches - * chroma-only edges. - * - * For quickstarters: just use luma edge detection. - * - * The general advice is to not rush the integration process and ensure each - * step is done correctly (don't try to integrate SMAA T2x with predicated edge - * detection from the start!). Ok then, let's go! - * - * 1. The first step is to create two RGBA temporal render targets for holding - * |edgesTex| and |blendTex|. - * - * In DX10 or DX11, you can use a RG render target for the edges texture. - * In the case of NVIDIA GPUs, using RG render targets seems to actually be - * slower. - * - * On the Xbox 360, you can use the same render target for resolving both - * |edgesTex| and |blendTex|, as they aren't needed simultaneously. - * - * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared - * each frame. Do not forget to clear the alpha channel! - * - * 3. The next step is loading the two supporting precalculated textures, - * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as - * C++ headers, and also as regular DDS files. They'll be needed for the - * 'SMAABlendingWeightCalculation' pass. - * - * If you use the C++ headers, be sure to load them in the format specified - * inside of them. - * - * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 - * respectively, if you have that option in your content processor pipeline. - * When compressing then, you get a non-perceptible quality decrease, and a - * marginal performance increase. - * - * 4. All samplers must be set to linear filtering and clamp. - * - * After you get the technique working, remember that 64-bit inputs have - * half-rate linear filtering on GCN. - * - * If SMAA is applied to 64-bit color buffers, switching to point filtering - * when accesing them will increase the performance. Search for - * 'SMAASamplePoint' to see which textures may benefit from point - * filtering, and where (which is basically the color input in the edge - * detection and resolve passes). - * - * 5. All texture reads and buffer writes must be non-sRGB, with the exception - * of the input read and the output write in - * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in - * this last pass are not possible, the technique will work anyway, but - * will perform antialiasing in gamma space. - * - * IMPORTANT: for best results the input read for the color/luma edge - * detection should *NOT* be sRGB. - * - * 6. Before including SMAA.h you'll have to setup the render target metrics, - * the target and any optional configuration defines. Optionally you can - * use a preset. - * - * You have the following targets available: - * SMAA_HLSL_3 - * SMAA_HLSL_4 - * SMAA_HLSL_4_1 - * SMAA_GLSL_3 * - * SMAA_GLSL_4 * - * - * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). - * - * And four presets: - * SMAA_PRESET_LOW (%60 of the quality) - * SMAA_PRESET_MEDIUM (%80 of the quality) - * SMAA_PRESET_HIGH (%95 of the quality) - * SMAA_PRESET_ULTRA (%99 of the quality) - * - * For example: - * #define SMAA_RT_METRICS vec4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) - * #define SMAA_GLSL_4 - * #define SMAA_PRESET_HIGH - * #include "SMAA.h" - * - * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a - * uniform variable. The code is designed to minimize the impact of not - * using a constant value, but it is still better to hardcode it. - * - * Depending on how you encoded 'areaTex' and 'searchTex', you may have to - * add (and customize) the following defines before including SMAA.h: - * #define SMAA_AREATEX_SELECT(sample) sample.rg - * #define SMAA_SEARCHTEX_SELECT(sample) sample.r - * - * If your engine is already using porting macros, you can define - * SMAA_CUSTOM_SL, and define the porting functions by yourself. - * - * 7. Then, you'll have to setup the passes as indicated in the scheme above. - * You can take a look into SMAA.fx, to see how we did it for our demo. - * Checkout the function wrappers, you may want to copy-paste them! - * - * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. - * You can use a screenshot from your engine to compare the |edgesTex| - * and |blendTex| produced inside of the engine with the results obtained - * with the reference demo. - * - * 9. After you get the last pass to work, it's time to optimize. You'll have - * to initialize a stencil buffer in the first pass (discard is already in - * the code), then mask execution by using it the second pass. The last - * pass should be executed in all pixels. - * - * - * After this point you can choose to enable predicated thresholding, - * temporal supersampling and motion blur integration: - * - * a) If you want to use predicated thresholding, take a look into - * SMAA_PREDICATION; you'll need to pass an extra texture in the edge - * detection pass. - * - * b) If you want to enable temporal supersampling (SMAA T2x): - * - * 1. The first step is to render using subpixel jitters. I won't go into - * detail, but it's as simple as moving each vertex position in the - * vertex shader, you can check how we do it in our DX10 demo. - * - * 2. Then, you must setup the temporal resolve. You may want to take a look - * into SMAAResolve for resolving 2x modes. After you get it working, you'll - * probably see ghosting everywhere. But fear not, you can enable the - * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. - * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. - * - * 3. The next step is to apply SMAA to each subpixel jittered frame, just as - * done for 1x. - * - * 4. At this point you should already have something usable, but for best - * results the proper area textures must be set depending on current jitter. - * For this, the parameter 'subsampleIndices' of - * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x - * mode: - * - * @SUBSAMPLE_INDICES - * - * | S# | Camera Jitter | subsampleIndices | - * +----+------------------+---------------------+ - * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | - * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | - * - * These jitter positions assume a bottom-to-top y axis. S# stands for the - * sample number. - * - * More information about temporal supersampling here: - * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf - * - * c) If you want to enable spatial multisampling (SMAA S2x): - * - * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be - * created with: - * - DX10: see below (*) - * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or - * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN - * - * This allows to ensure that the subsample order matches the table in - * @SUBSAMPLE_INDICES. - * - * (*) In the case of DX10, we refer the reader to: - * - SMAA::detectMSAAOrder and - * - SMAA::msaaReorder - * - * These functions allow to match the standard multisample patterns by - * detecting the subsample order for a specific GPU, and reordering - * them appropriately. - * - * 2. A shader must be run to output each subsample into a separate buffer - * (DX10 is required). You can use SMAASeparate for this purpose, or just do - * it in an existing pass (for example, in the tone mapping pass, which has - * the advantage of feeding tone mapped subsamples to SMAA, which will yield - * better results). - * - * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing - * the results in the final buffer. The second run should alpha blend with - * the existing final buffer using a blending factor of 0.5. - * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point - * b). - * - * d) If you want to enable temporal supersampling on top of SMAA S2x - * (which actually is SMAA 4x): - * - * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is - * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' - * must be set as follows: - * - * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | - * +----+----+--------------------+-------------------+----------------------+ - * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | - * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | - * +----+----+--------------------+-------------------+----------------------+ - * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | - * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | - * - * These jitter positions assume a bottom-to-top y axis. F# stands for the - * frame number. S# stands for the sample number. - * - * 2. After calculating SMAA S2x for current frame (with the new subsample - * indices), previous frame must be reprojected as in SMAA T2x mode (see - * point b). - * - * e) If motion blur is used, you may want to do the edge detection pass - * together with motion blur. This has two advantages: - * - * 1. Pixels under heavy motion can be omitted from the edge detection process. - * For these pixels we can just store "no edge", as motion blur will take - * care of them. - * 2. The center pixel tap is reused. - * - * Note that in this case depth testing should be used instead of stenciling, - * as we have to write all the pixels in the motion blur pass. - * - * That's it! - */ - -//----------------------------------------------------------------------------- -// SMAA Presets - -/** - * Note that if you use one of these presets, the following configuration - * macros will be ignored if set in the "Configurable Defines" section. - */ - -#if defined(SMAA_PRESET_LOW) -#define SMAA_THRESHOLD 0.15 -#define SMAA_MAX_SEARCH_STEPS 4 -#define SMAA_DISABLE_DIAG_DETECTION -#define SMAA_DISABLE_CORNER_DETECTION -#elif defined(SMAA_PRESET_MEDIUM) -#define SMAA_THRESHOLD 0.1 -#define SMAA_MAX_SEARCH_STEPS 8 -#define SMAA_DISABLE_DIAG_DETECTION -#define SMAA_DISABLE_CORNER_DETECTION -#elif defined(SMAA_PRESET_HIGH) -#define SMAA_THRESHOLD 0.1 -#define SMAA_MAX_SEARCH_STEPS 16 -#define SMAA_MAX_SEARCH_STEPS_DIAG 8 -#define SMAA_CORNER_ROUNDING 25 -#elif defined(SMAA_PRESET_ULTRA) -#define SMAA_THRESHOLD 0.05 -#define SMAA_MAX_SEARCH_STEPS 32 -#define SMAA_MAX_SEARCH_STEPS_DIAG 16 -#define SMAA_CORNER_ROUNDING 25 -#endif - -//----------------------------------------------------------------------------- -// Configurable Defines - -/** - * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. - * Lowering this value you will be able to detect more edges at the expense of - * performance. - * - * Range: [0, 0.5] - * 0.1 is a reasonable value, and allows to catch most visible edges. - * 0.05 is a rather overkill value, that allows to catch 'em all. - * - * If temporal supersampling is used, 0.2 could be a reasonable value, as low - * contrast edges are properly filtered by just 2x. - */ -#ifndef SMAA_THRESHOLD -#define SMAA_THRESHOLD 0.1 -#endif - -/** - * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. - * - * Range: depends on the depth range of the scene. - */ -#ifndef SMAA_DEPTH_THRESHOLD -#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) -#endif - -/** - * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the - * horizontal/vertical pattern searches, at each side of the pixel. - * - * In number of pixels, it's actually the double. So the maximum line length - * perfectly handled by, for example 16, is 64 (by perfectly, we meant that - * longer lines won't look as good, but still antialiased). - * - * Range: [0, 112] - */ -#ifndef SMAA_MAX_SEARCH_STEPS -#define SMAA_MAX_SEARCH_STEPS 16 -#endif - -/** - * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the - * diagonal pattern searches, at each side of the pixel. In this case we jump - * one pixel at time, instead of two. - * - * Range: [0, 20] - * - * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 - * steps), but it can have a significant impact on older machines. - * - * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. - */ -#ifndef SMAA_MAX_SEARCH_STEPS_DIAG -#define SMAA_MAX_SEARCH_STEPS_DIAG 8 -#endif - -/** - * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. - * - * Range: [0, 100] - * - * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. - */ -#ifndef SMAA_CORNER_ROUNDING -#define SMAA_CORNER_ROUNDING 25 -#endif - -/** - * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times - * bigger contrast than current edge, current edge will be discarded. - * - * This allows to eliminate spurious crossing edges, and is based on the fact - * that, if there is too much contrast in a direction, that will hide - * perceptually contrast in the other neighbors. - */ -#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR -#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 -#endif - -/** - * Predicated thresholding allows to better preserve texture details and to - * improve performance, by decreasing the number of detected edges using an - * additional buffer like the light accumulation buffer, object ids or even the - * depth buffer (the depth buffer usage may be limited to indoor or short range - * scenes). - * - * It locally decreases the luma or color threshold if an edge is found in an - * additional buffer (so the global threshold can be higher). - * - * This method was developed by Playstation EDGE MLAA team, and used in - * Killzone 3, by using the light accumulation buffer. More information here: - * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx - */ -#ifndef SMAA_PREDICATION -#define SMAA_PREDICATION 0 -#endif - -/** - * Threshold to be used in the additional predication buffer. - * - * Range: depends on the input, so you'll have to find the magic number that - * works for you. - */ -#ifndef SMAA_PREDICATION_THRESHOLD -#define SMAA_PREDICATION_THRESHOLD 0.01 -#endif - -/** - * How much to scale the global threshold used for luma or color edge - * detection when using predication. - * - * Range: [1, 5] - */ -#ifndef SMAA_PREDICATION_SCALE -#define SMAA_PREDICATION_SCALE 2.0 -#endif - -/** - * How much to locally decrease the threshold. - * - * Range: [0, 1] - */ -#ifndef SMAA_PREDICATION_STRENGTH -#define SMAA_PREDICATION_STRENGTH 0.4 -#endif - -/** - * Temporal reprojection allows to remove ghosting artifacts when using - * temporal supersampling. We use the CryEngine 3 method which also introduces - * velocity weighting. This feature is of extreme importance for totally - * removing ghosting. More information here: - * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf - * - * Note that you'll need to setup a velocity buffer for enabling reprojection. - * For static geometry, saving the previous depth buffer is a viable - * alternative. - */ -#ifndef SMAA_REPROJECTION -#define SMAA_REPROJECTION 0 -#endif - -/** - * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to - * remove ghosting trails behind the moving object, which are not removed by - * just using reprojection. Using low values will exhibit ghosting, while using - * high values will disable temporal supersampling under motion. - * - * Behind the scenes, velocity weighting removes temporal supersampling when - * the velocity of the subsamples differs (meaning they are different objects). - * - * Range: [0, 80] - */ -#ifndef SMAA_REPROJECTION_WEIGHT_SCALE -#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 -#endif - -/** - * On some compilers, discard cannot be used in vertex shaders. Thus, they need - * to be compiled separately. - */ -#ifndef SMAA_INCLUDE_VS -#define SMAA_INCLUDE_VS 1 -#endif -#ifndef SMAA_INCLUDE_PS -#define SMAA_INCLUDE_PS 1 -#endif - -//----------------------------------------------------------------------------- -// Texture Access Defines - -#ifndef SMAA_AREATEX_SELECT -#if defined(SMAA_HLSL_3) -#define SMAA_AREATEX_SELECT(sample) sample.ra -#else -#define SMAA_AREATEX_SELECT(sample) sample.rg -#endif -#endif - -#ifndef SMAA_SEARCHTEX_SELECT -#define SMAA_SEARCHTEX_SELECT(sample) sample.r -#endif - -#ifndef SMAA_DECODE_VELOCITY -#define SMAA_DECODE_VELOCITY(sample) sample.rg -#endif - -//----------------------------------------------------------------------------- -// Non-Configurable Defines - -#define SMAA_AREATEX_MAX_DISTANCE 16 -#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 -#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0)) -#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) -#define SMAA_SEARCHTEX_SIZE vec2(66.0, 33.0) -#define SMAA_SEARCHTEX_PACKED_SIZE vec2(64.0, 16.0) -#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) - -//----------------------------------------------------------------------------- -// Porting Functions - -#if defined(SMAA_HLSL_3) -#define SMAATexture2D(tex) sampler2D tex -#define SMAATexturePass2D(tex) tex -#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) -#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) -#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) -#define SMAASample(tex, coord) tex2D(tex, coord) -#define SMAASamplePoint(tex, coord) tex2D(tex, coord) -#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) -#define SMAA_FLATTEN [flatten] -#define SMAA_BRANCH [branch] -#endif -#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) -SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; -SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; -#define SMAATexture2D(tex) Texture2D tex -#define SMAATexturePass2D(tex) tex -#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) -#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) -#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) -#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) -#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) -#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) -#define SMAA_FLATTEN [flatten] -#define SMAA_BRANCH [branch] -#define SMAATexture2DMS2(tex) Texture2DMS tex -#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) -#if defined(SMAA_HLSL_4_1) -#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) -#endif -#endif -#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) -#define SMAATexture2D(tex) sampler2D tex -#define SMAATexturePass2D(tex) tex -#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) -#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) -#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) -#define SMAASample(tex, coord) texture(tex, coord) -#define SMAASamplePoint(tex, coord) texture(tex, coord) -#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) -#define SMAA_FLATTEN -#define SMAA_BRANCH -#define lerp(a, b, t) mix(a, b, t) -#define saturate(a) clamp(a, 0.0, 1.0) -#if defined(SMAA_GLSL_4) -#define mad(a, b, c) fma(a, b, c) -#define SMAAGather(tex, coord) textureGather(tex, coord) -#define SMAAGather(tex, coord) textureGather(tex, coord) -#else -#define mad(a, b, c) (a * b + c) -#endif -#define float2 vec2 -#define float3 vec3 -#define float4 vec4 -#define int2 ivec2 -#define int3 ivec3 -#define int4 ivec4 -#define bool2 bvec2 -#define bool3 bvec3 -#define bool4 bvec4 -#endif - -#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) -#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL -#endif - -/** - * Gathers current pixel, and the top-left neighbors. - */ - vec3 SMAAGatherNeighbours(vec2 coord, vec4 offset[3], sampler2D tex) - { - float P = texture(tex, coord).r; - float Pleft = texture(tex, offset[0].xy).r; - float Ptop = texture(tex, offset[0].zw).r; - return vec3(P, Pleft, Ptop); - } - -/** - * Adjusts the threshold by means of predication. - */ - vec3 SMAACalculatePredicatedThreshold(vec2 coord, vec4 offset[3], sampler2D predicationTex) - { - vec3 neighbours = SMAAGatherNeighbours(coord, offset, predicationTex); - vec2 delta = abs(neighbours.xx - neighbours.yz); - vec2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); - return vec3(SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges), 1.0); - } - -/** - * Conditional move: - */ - void SMAAMovc(bvec2 cond, inout vec2 variable, vec2 value) { - if (cond.x) variable.x = value.x; - if (cond.y) variable.y = value.y; -} - -void SMAAMovc(bvec4 cond, inout vec4 variable, vec4 value) { - SMAAMovc(cond.xy, variable.xy, value.xy); - SMAAMovc(cond.zw, variable.zw, value.zw); -} \ No newline at end of file diff --git a/anti-aliasing/shaders/smaa/smaa-edge-detection.slang b/anti-aliasing/shaders/smaa/smaa-edge-detection.slang deleted file mode 100644 index 6b168ab..0000000 --- a/anti-aliasing/shaders/smaa/smaa-edge-detection.slang +++ /dev/null @@ -1,185 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; -} params; - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; -} global; - -#include "smaa-common.h" - -#pragma stage vertex -layout(location = 0) in vec4 Position; -layout(location = 1) in vec2 TexCoord; -layout(location = 0) out vec2 texcoord; -layout(location = 1) out vec4 offset[3]; - -void main() -{ - gl_Position = global.MVP * Position; - texcoord = TexCoord; - offset[0] = fma(SMAA_RT_METRICS.xyxy, vec4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); - offset[1] = fma(SMAA_RT_METRICS.xyxy, vec4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); - offset[2] = fma(SMAA_RT_METRICS.xyxy, vec4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); -} - -#pragma stage fragment -layout(location = 0) in vec2 texcoord; -layout(location = 1) in vec4 offset[3]; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - -/** - * Luma Edge Detection - * - * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and - * thus 'colorTex' should be a non-sRGB texture. - */ -vec2 SMAALumaEdgeDetectionPS(vec2 texcoord, vec4 offset[3], sampler2D colorTex - #if SMAA_PREDICATION - , SMAATexture2D(predicationTex) - #endif - ) { - // Calculate the threshold: - #if SMAA_PREDICATION - vec2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); - #else - vec2 threshold = vec2(SMAA_THRESHOLD, SMAA_THRESHOLD); - #endif - - // Calculate lumas: - vec3 weights = vec3(0.2126, 0.7152, 0.0722); - float L = dot(texture(colorTex, texcoord).rgb, weights); - - float Lleft = dot(texture(colorTex, offset[0].xy).rgb, weights); - float Ltop = dot(texture(colorTex, offset[0].zw).rgb, weights); - - // We do the usual threshold: - vec4 delta; - delta.xy = abs(L - vec2(Lleft, Ltop)); - vec2 edges = step(threshold, delta.xy); - - // Then discard if there is no edge: - if (dot(edges, vec2(1.0, 1.0)) == 0.0) - discard; - - // Calculate right and bottom deltas: - float Lright = dot(texture(colorTex, offset[1].xy).rgb, weights); - float Lbottom = dot(texture(colorTex, offset[1].zw).rgb, weights); - delta.zw = abs(L - vec2(Lright, Lbottom)); - - // Calculate the maximum delta in the direct neighborhood: - vec2 maxDelta = max(delta.xy, delta.zw); - - // Calculate left-left and top-top deltas: - float Lleftleft = dot(texture(colorTex, offset[2].xy).rgb, weights); - float Ltoptop = dot(texture(colorTex, offset[2].zw).rgb, weights); - delta.zw = abs(vec2(Lleft, Ltop) - vec2(Lleftleft, Ltoptop)); - - // Calculate the final maximum delta: - maxDelta = max(maxDelta.xy, delta.zw); - float finalDelta = max(maxDelta.x, maxDelta.y); - - // Local contrast adaptation: - edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); - - return edges; -} - -/** - * Color Edge Detection - * - * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and - * thus 'colorTex' should be a non-sRGB texture. - */ -vec2 SMAAColorEdgeDetectionPS(vec2 texcoord, - vec4 offset[3], - sampler2D colorTex - #if SMAA_PREDICATION - , sampler2D predicationTex - #endif - ) { - // Calculate the threshold: - #if SMAA_PREDICATION - vec2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); - #else - vec2 threshold = vec2(SMAA_THRESHOLD, SMAA_THRESHOLD); - #endif - - // Calculate color deltas: - vec4 delta; - vec3 C = texture(colorTex, texcoord).rgb; - - vec3 Cleft = texture(colorTex, offset[0].xy).rgb; - vec3 t = abs(C - Cleft); - delta.x = max(max(t.r, t.g), t.b); - - vec3 Ctop = texture(colorTex, offset[0].zw).rgb; - t = abs(C - Ctop); - delta.y = max(max(t.r, t.g), t.b); - - // We do the usual threshold: - vec2 edges = step(threshold, delta.xy); - - // Then discard if there is no edge: - if (dot(edges, vec2(1.0, 1.0)) == 0.0) - discard; - - // Calculate right and bottom deltas: - vec3 Cright = texture(colorTex, offset[1].xy).rgb; - t = abs(C - Cright); - delta.z = max(max(t.r, t.g), t.b); - - vec3 Cbottom = texture(colorTex, offset[1].zw).rgb; - t = abs(C - Cbottom); - delta.w = max(max(t.r, t.g), t.b); - - // Calculate the maximum delta in the direct neighborhood: - vec2 maxDelta = max(delta.xy, delta.zw); - - // Calculate left-left and top-top deltas: - vec3 Cleftleft = texture(colorTex, offset[2].xy).rgb; - t = abs(C - Cleftleft); - delta.z = max(max(t.r, t.g), t.b); - - vec3 Ctoptop = texture(colorTex, offset[2].zw).rgb; - t = abs(C - Ctoptop); - delta.w = max(max(t.r, t.g), t.b); - - // Calculate the final maximum delta: - maxDelta = max(maxDelta.xy, delta.zw); - float finalDelta = max(maxDelta.x, maxDelta.y); - - // Local contrast adaptation: - edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); - - return edges; -} - -/** - * Depth Edge Detection - */ -vec2 SMAADepthEdgeDetectionPS(vec2 texcoord, - vec4 offset[3], - sampler2D depthTex) { - vec3 neighbours = SMAAGatherNeighbours(texcoord, offset, depthTex); - vec2 delta = abs(neighbours.xx - vec2(neighbours.y, neighbours.z)); - vec2 edges = step(SMAA_DEPTH_THRESHOLD, delta); - - if (dot(edges, vec2(1.0, 1.0)) == 0.0) - discard; - - return edges; -} - -void main() -{ - FragColor = vec4(SMAALumaEdgeDetectionPS(texcoord, offset, Source), 0.0, 0.0); -} \ No newline at end of file diff --git a/anti-aliasing/shaders/smaa/smaa-neighborhood-blending.slang b/anti-aliasing/shaders/smaa/smaa-neighborhood-blending.slang deleted file mode 100644 index b3f11c6..0000000 --- a/anti-aliasing/shaders/smaa/smaa-neighborhood-blending.slang +++ /dev/null @@ -1,102 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; -} params; - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; -} global; - -#include "smaa-common.h" - -#pragma stage vertex -layout(location = 0) in vec4 Position; -layout(location = 1) in vec2 TexCoord; -layout(location = 0) out vec2 texcoord; -layout(location = 1) out vec4 offset; - -void main() -{ - gl_Position = global.MVP * Position; - texcoord = TexCoord; - offset = fma(SMAA_RT_METRICS.xyxy, vec4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); -} - -#pragma stage fragment -layout(location = 0) in vec2 texcoord; -layout(location = 1) in vec4 offset; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; -layout(set = 0, binding = 3) uniform sampler2D Original; - -//----------------------------------------------------------------------------- -// Neighborhood Blending Pixel Shader (Third Pass) - -vec4 SMAANeighborhoodBlendingPS(vec2 texcoord, - vec4 offset, - sampler2D colorTex, - sampler2D blendTex - #if SMAA_REPROJECTION - , SMAATexture2D(velocityTex) - #endif - ) { - // Fetch the blending weights for current pixel: - vec4 a; - a.x = texture(blendTex, offset.xy).a; // Right - a.y = texture(blendTex, offset.zw).g; // Top - a.wz = texture(blendTex, texcoord).xz; // Bottom / Left - - // Is there any blending weight with a value greater than 0.0? -// SMAA_BRANCH - if (dot(a, vec4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { - vec4 color = textureLod(colorTex, texcoord, 0.0); - - #if SMAA_REPROJECTION - vec2 velocity = SMAA_DECODE_VELOCITY(textureLod(velocityTex, texcoord, 0.0)); - - // Pack velocity into the alpha channel: - color.a = sqrt(5.0 * length(velocity)); - #endif - - return color; - } else { - bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) - - // Calculate the blending offsets: - vec4 blendingOffset = vec4(0.0, a.y, 0.0, a.w); - vec2 blendingWeight = a.yw; - SMAAMovc(bvec4(h, h, h, h), blendingOffset, vec4(a.x, 0.0, a.z, 0.0)); - SMAAMovc(bvec2(h, h), blendingWeight, a.xz); - blendingWeight /= dot(blendingWeight, vec2(1.0, 1.0)); - - // Calculate the texture coordinates: - vec4 blendingCoord = fma(blendingOffset, vec4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); - - // We exploit bilinear filtering to mix current pixel with the chosen - // neighbor: - vec4 color = blendingWeight.x * textureLod(colorTex, blendingCoord.xy, 0.0); - color += blendingWeight.y * textureLod(colorTex, blendingCoord.zw, 0.0); - - #if SMAA_REPROJECTION - // Antialias velocity for proper reprojection in a later stage: - vec2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(textureLod(velocityTex, blendingCoord.xy, 0.0)); - velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(textureLod(velocityTex, blendingCoord.zw, 0.0)); - - // Pack velocity into the alpha channel: - color.a = sqrt(5.0 * length(velocity)); - #endif - - return color; - } -} - -void main() -{ - FragColor = SMAANeighborhoodBlendingPS(texcoord, offset, Original, Source); -} \ No newline at end of file diff --git a/anti-aliasing/shaders/smaa/smaa-pass0.slang b/anti-aliasing/shaders/smaa/smaa-pass0.slang new file mode 100644 index 0000000..4efdc5a --- /dev/null +++ b/anti-aliasing/shaders/smaa/smaa-pass0.slang @@ -0,0 +1,54 @@ +#version 450 +#pragma name SMAA_Pass0 +//----------------------------------------------------------------------------- +// Edge Detection Shaders (First Pass) + +#pragma parameter SMAA_EDT "SMAA Edge Detection: Luma | Color" 0.0 0.0 1.0 1.0 + +layout(push_constant) uniform Push { + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float SMAA_EDT; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO { + mat4 MVP; +} global; + +#define SMAA_RT_METRICS vec4(params.SourceSize.z, params.SourceSize.w, params.SourceSize.x, params.SourceSize.y) +#define SMAA_GLSL_4 +#define SMAA_INCLUDE_PS 0 +#include "SMAA.hlsl" + +#pragma stage vertex +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; +layout(location = 1) out vec4 offset[3]; + +void main() { + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; + SMAAEdgeDetectionVS(TexCoord, offset); +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 1) in vec4 offset[3]; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +#include "SMAA.frag" + +void main() { + if (params.SMAA_EDT == 0.0) { + FragColor = vec4(SMAALumaEdgeDetectionPS(vTexCoord, offset, Source), 0.0, 0.0); + } else if (params.SMAA_EDT <= 1.0) { + FragColor = vec4(SMAAColorEdgeDetectionPS(vTexCoord, offset, Source), 0.0, 0.0); + // Unavailable as we don't have access to a depth buffer (yet?) + // } else if (params.SMAA_EDT <= 2.0) { + // FragColor = vec4(SMAADepthEdgeDetectionPS(vTexCoord, offset, depthTex), 0.0, 0.0); + } +} diff --git a/anti-aliasing/shaders/smaa/smaa-pass1.slang b/anti-aliasing/shaders/smaa/smaa-pass1.slang new file mode 100644 index 0000000..3508492 --- /dev/null +++ b/anti-aliasing/shaders/smaa/smaa-pass1.slang @@ -0,0 +1,49 @@ +#version 450 +#pragma name SMAA_Pass1 +//----------------------------------------------------------------------------- +// Blending Weight Calculation Shader (Second Pass) + +layout(push_constant) uniform Push { + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO { + mat4 MVP; +} global; + +#define SMAA_RT_METRICS vec4(params.SourceSize.z, params.SourceSize.w, params.SourceSize.x, params.SourceSize.y) +#define SMAA_GLSL_4 +#define SMAA_INCLUDE_PS 0 +#include "SMAA.hlsl" + +#pragma stage vertex +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; +layout(location = 1) out vec2 pixcoord; +layout(location = 2) out vec4 offset[3]; + +void main() { + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; + SMAABlendingWeightCalculationVS(TexCoord, pixcoord, offset); +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 1) in vec2 pixcoord; +layout(location = 2) in vec4 offset[3]; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; +layout(set = 0, binding = 3) uniform sampler2D areaTex; +layout(set = 0, binding = 4) uniform sampler2D searchTex; + +#include "SMAA.frag" + +void main() { + vec4 subsampleIndices = vec4(0.0); + FragColor = SMAABlendingWeightCalculationPS(vTexCoord, pixcoord, offset, Source, areaTex, searchTex, subsampleIndices); +} diff --git a/anti-aliasing/shaders/smaa/smaa-pass2.slang b/anti-aliasing/shaders/smaa/smaa-pass2.slang new file mode 100644 index 0000000..556b062 --- /dev/null +++ b/anti-aliasing/shaders/smaa/smaa-pass2.slang @@ -0,0 +1,45 @@ +#version 450 +#pragma name SMAA_Pass2 +//----------------------------------------------------------------------------- +// Neighborhood Blending Shader (Third Pass) + +layout(push_constant) uniform Push { + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO { + mat4 MVP; +} global; + +#define SMAA_RT_METRICS vec4(params.SourceSize.z, params.SourceSize.w, params.SourceSize.x, params.SourceSize.y) +#define SMAA_GLSL_4 +#define SMAA_INCLUDE_PS 0 +#include "SMAA.hlsl" + +#pragma stage vertex +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; +layout(location = 1) out vec4 offset; + +void main() { + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; + SMAANeighborhoodBlendingVS(TexCoord, offset); +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 1) in vec4 offset; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; +layout(set = 0, binding = 3) uniform sampler2D SMAA_Input; + +#include "SMAA.frag" + +void main() { + FragColor = SMAANeighborhoodBlendingPS(vTexCoord, offset, SMAA_Input, Source); +} diff --git a/anti-aliasing/smaa+linear.slangp b/anti-aliasing/smaa+linear.slangp new file mode 100644 index 0000000..ddac65a --- /dev/null +++ b/anti-aliasing/smaa+linear.slangp @@ -0,0 +1,32 @@ +shaders = 5 + +shader0 = ../stock.slang +filter_linear0 = false +scale_type0 = source +scale0 = 1.0 +alias0 = SMAA_Input + +shader1 = shaders/smaa/smaa-pass0.slang +filter_linear1 = true +scale_type1 = source +scale1 = 1.0 + +shader2 = shaders/smaa/smaa-pass1.slang +filter_linear2 = true +scale_type2 = source +scale2 = 1.0 + +shader3 = shaders/smaa/smaa-pass2.slang +filter_linear3 = true +scale_type3 = source +scale3 = 1.0 + +textures = "areaTex;searchTex" +areaTex = shaders/smaa/AreaTex.png +searchTex = shaders/smaa/SearchTex.png + +shader4 = ../stock.slang +filter_linear4 = true + +parameters = "SMAA_EDT" +SMAA_EDT = 0.0 diff --git a/anti-aliasing/smaa+sharpen.slangp b/anti-aliasing/smaa+sharpen.slangp new file mode 100644 index 0000000..31d82f4 --- /dev/null +++ b/anti-aliasing/smaa+sharpen.slangp @@ -0,0 +1,37 @@ +shaders = 5 + +shader0 = ../stock.slang +filter_linear0 = false +scale_type0 = source +scale0 = 1.0 +alias0 = SMAA_Input + +shader1 = shaders/smaa/smaa-pass0.slang +filter_linear1 = true +scale_type1 = source +scale1 = 1.0 + +shader2 = shaders/smaa/smaa-pass1.slang +filter_linear2 = true +scale_type2 = source +scale2 = 1.0 + +shader3 = shaders/smaa/smaa-pass2.slang +filter_linear3 = true +scale_type3 = source +scale3 = 1.0 + +textures = "areaTex;searchTex" +areaTex = shaders/smaa/AreaTex.png +searchTex = shaders/smaa/SearchTex.png + +shader4 = ../sharpen/shaders/fast-sharpen.slang +filter_linear4 = false +scale_type4 = source +scale4 = 1.0 + +parameters = "SMAA_EDT;SHARPEN;CONTR;DETAILS" +SMAA_EDT = 1.0 +SHARPEN = 0.9 +CONTR = 0.01 +DETAILS = 0.2 diff --git a/anti-aliasing/smaa.slangp b/anti-aliasing/smaa.slangp index d65b1fc..5163548 100644 --- a/anti-aliasing/smaa.slangp +++ b/anti-aliasing/smaa.slangp @@ -1,18 +1,29 @@ -shaders = 3 +shaders = 4 -shader0 = shaders/smaa/smaa-edge-detection.slang -filter_linear0 = true +shader0 = ../stock.slang +filter_linear0 = false scale_type0 = source scale0 = 1.0 +alias0 = SMAA_Input -shader1 = shaders/smaa/smaa-blend-weight-calculation.slang +shader1 = shaders/smaa/smaa-pass0.slang filter_linear1 = true scale_type1 = source scale1 = 1.0 -shader2 = shaders/smaa/smaa-neighborhood-blending.slang +shader2 = shaders/smaa/smaa-pass1.slang filter_linear2 = true +scale_type2 = source +scale2 = 1.0 + +shader3 = shaders/smaa/smaa-pass2.slang +filter_linear3 = true +scale_type3 = source +scale3 = 1.0 textures = "areaTex;searchTex" areaTex = shaders/smaa/AreaTex.png -searchTex = shaders/smaa/SearchTex.png \ No newline at end of file +searchTex = shaders/smaa/SearchTex.png + +parameters = "SMAA_EDT" +SMAA_EDT = 0.0