SMAA: Expose more quality settings

- Expose all the configuration settings used by the quality presets to allow fine-tuning of edge detection.
- Changed default edge detection technique from luma to color since in my tests that always yields better results.
- Increased quality to ultra in default preset.
- Deleted the duplication of the fragment shader part of the lib, as I figured out how to include the lib twice with different defines for the vertex and fragment passes. That is necessary probably because discard cannot be used in vertex shaders in RetroArch, indicated in a comment in the lib as a possible restriction in some compilers.
- Include license headers to make clear that all my contributions are released on the public domain under the terms of the Unlicense. Anyone can do whatever they want with this.
This commit is contained in:
Jonatas Esteves 2021-09-08 14:48:23 -03:00
parent 788f73b930
commit dd9994482c
7 changed files with 69 additions and 725 deletions

View file

@ -614,7 +614,7 @@ layout(push_constant) uniform Push
### Samplers
Which samplers are used for textures are specified by the preset format.
The sampler remains constant throughout the frame, there is currently no way to select samplers on a frame-by-frame basic.
The sampler remains constant throughout the frame, there is currently no way to select samplers on a frame-by-frame basis.
This is mostly to make it possible to use the spec in GLES2 as GLES2 has no concept of separate samplers and images.
### sRGB

View file

@ -1,710 +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.
*/
//-----------------------------------------------------------------------------
// 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
//-----------------------------------------------------------------------------

View file

@ -1,9 +1,14 @@
#version 450
// SPDX-License-Identifier: Unlicense
#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
#pragma parameter SMAA_EDT "SMAA Edge Detection: Luma | Color" 1.0 0.0 1.0 1.0
#pragma parameter SMAA_THRESHOLD "SMAA Threshold" 0.05 0.01 0.5 0.01
#pragma parameter SMAA_MAX_SEARCH_STEPS "SMAA Max Search Steps" 32.0 4.0 112.0 1.0
#pragma parameter SMAA_MAX_SEARCH_STEPS_DIAG "SMAA Max Search Steps Diagonal" 16.0 4.0 20.0 1.0
#pragma parameter SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR "SMAA Local Contrast Adapt. Factor" 2.0 1.0 4.0 0.1
layout(push_constant) uniform Push {
vec4 SourceSize;
@ -11,6 +16,10 @@ layout(push_constant) uniform Push {
vec4 OutputSize;
uint FrameCount;
float SMAA_EDT;
float SMAA_THRESHOLD;
float SMAA_MAX_SEARCH_STEPS;
float SMAA_MAX_SEARCH_STEPS_DIAG;
float SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR;
} params;
layout(std140, set = 0, binding = 0) uniform UBO {
@ -19,8 +28,15 @@ layout(std140, set = 0, binding = 0) uniform UBO {
#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"
float THRESHOLD = params.SMAA_THRESHOLD;
float MAX_SEARCH_STEPS = params.SMAA_MAX_SEARCH_STEPS;
float MAX_SEARCH_STEPS_DIAG = params.SMAA_MAX_SEARCH_STEPS_DIAG;
float LOCAL_CONTRAST_ADAPTATION_FACTOR = params.SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR;
#define SMAA_THRESHOLD THRESHOLD
#define SMAA_MAX_SEARCH_STEPS MAX_SEARCH_STEPS
#define SMAA_MAX_SEARCH_STEPS_DIAG MAX_SEARCH_STEPS_DIAG
#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR LOCAL_CONTRAST_ADAPTATION_FACTOR
#pragma stage vertex
layout(location = 0) in vec4 Position;
@ -28,6 +44,9 @@ layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out vec4 offset[3];
#define SMAA_INCLUDE_PS 0
#include "SMAA.hlsl"
void main() {
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
@ -40,7 +59,8 @@ layout(location = 1) in vec4 offset[3];
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
#include "SMAA.frag"
#define SMAA_INCLUDE_VS 0
#include "SMAA.hlsl"
void main() {
if (params.SMAA_EDT == 0.0) {

View file

@ -1,13 +1,23 @@
#version 450
// SPDX-License-Identifier: Unlicense
#pragma name SMAA_Pass1
//-----------------------------------------------------------------------------
// Blending Weight Calculation Shader (Second Pass)
#pragma parameter SMAA_THRESHOLD "SMAA Threshold" 0.05 0.01 0.5 0.01
#pragma parameter SMAA_MAX_SEARCH_STEPS "SMAA Max Search Steps" 32.0 4.0 112.0 1.0
#pragma parameter SMAA_MAX_SEARCH_STEPS_DIAG "SMAA Max Search Steps Diagonal" 16.0 4.0 20.0 1.0
#pragma parameter SMAA_CORNER_ROUNDING "SMAA Corner Rounding" 25.0 0.0 100.0 1.0
layout(push_constant) uniform Push {
vec4 SourceSize;
vec4 OriginalSize;
vec4 OutputSize;
uint FrameCount;
float SMAA_THRESHOLD;
float SMAA_MAX_SEARCH_STEPS;
float SMAA_MAX_SEARCH_STEPS_DIAG;
float SMAA_CORNER_ROUNDING;
} params;
layout(std140, set = 0, binding = 0) uniform UBO {
@ -16,8 +26,15 @@ layout(std140, set = 0, binding = 0) uniform UBO {
#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"
float THRESHOLD = params.SMAA_THRESHOLD;
float MAX_SEARCH_STEPS = params.SMAA_MAX_SEARCH_STEPS;
float MAX_SEARCH_STEPS_DIAG = params.SMAA_MAX_SEARCH_STEPS_DIAG;
float CORNER_ROUNDING = params.SMAA_CORNER_ROUNDING;
#define SMAA_THRESHOLD THRESHOLD
#define SMAA_MAX_SEARCH_STEPS MAX_SEARCH_STEPS
#define SMAA_MAX_SEARCH_STEPS_DIAG MAX_SEARCH_STEPS_DIAG
#define SMAA_CORNER_ROUNDING CORNER_ROUNDING
#pragma stage vertex
layout(location = 0) in vec4 Position;
@ -26,6 +43,9 @@ layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out vec2 pixcoord;
layout(location = 2) out vec4 offset[3];
#define SMAA_INCLUDE_PS 0
#include "SMAA.hlsl"
void main() {
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
@ -41,7 +61,8 @@ 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"
#define SMAA_INCLUDE_VS 0
#include "SMAA.hlsl"
void main() {
vec4 subsampleIndices = vec4(0.0);

View file

@ -1,4 +1,5 @@
#version 450
// SPDX-License-Identifier: Unlicense
#pragma name SMAA_Pass2
//-----------------------------------------------------------------------------
// Neighborhood Blending Shader (Third Pass)
@ -16,8 +17,6 @@ layout(std140, set = 0, binding = 0) uniform UBO {
#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;
@ -25,6 +24,9 @@ layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out vec4 offset;
#define SMAA_INCLUDE_PS 0
#include "SMAA.hlsl"
void main() {
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
@ -38,7 +40,8 @@ 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"
#define SMAA_INCLUDE_VS 0
#include "SMAA.hlsl"
void main() {
FragColor = SMAANeighborhoodBlendingPS(vTexCoord, offset, SMAA_Input, Source);

View file

@ -30,8 +30,13 @@ filter_linear4 = false
scale_type4 = source
scale4 = 1.0
parameters = "SMAA_EDT;SHARPEN;CONTR;DETAILS"
parameters = "SMAA_EDT;SMAA_THRESHOLD;SMAA_MAX_SEARCH_STEPS;SMAA_MAX_SEARCH_STEPS_DIAG;SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR;SMAA_CORNER_ROUNDING;SHARPEN;CONTR;DETAILS"
SMAA_EDT = 1.0
SMAA_THRESHOLD = 0.05
SMAA_MAX_SEARCH_STEPS = 40
SMAA_MAX_SEARCH_STEPS_DIAG = 20
SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR = 2.5
SMAA_CORNER_ROUNDING = 50.0
SHARPEN = 0.9
CONTR = 0.01
CONTR = 0.0
DETAILS = 0.2

View file

@ -25,5 +25,10 @@ textures = "areaTex;searchTex"
areaTex = shaders/smaa/AreaTex.png
searchTex = shaders/smaa/SearchTex.png
parameters = "SMAA_EDT"
SMAA_EDT = 0.0
parameters = "SMAA_EDT;SMAA_THRESHOLD;SMAA_MAX_SEARCH_STEPS;SMAA_MAX_SEARCH_STEPS_DIAG;SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR;SMAA_CORNER_ROUNDING"
SMAA_EDT = 1.0
SMAA_THRESHOLD = 0.05
SMAA_MAX_SEARCH_STEPS = 32
SMAA_MAX_SEARCH_STEPS_DIAG = 16
SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR = 2.0
SMAA_CORNER_ROUNDING = 25.0