-
Notifications
You must be signed in to change notification settings - Fork 157
Expand file tree
/
Copy pathfxaa.frag
More file actions
217 lines (172 loc) · 8.37 KB
/
fxaa.frag
File metadata and controls
217 lines (172 loc) · 8.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#version 330
in INTERFACE {
vec2 uv;
} In ;
uniform sampler2D screenTexture;
uniform vec2 inverseScreenSize;
out vec4 fragColor;
// Settings for FXAA.
#define EDGE_THRESHOLD_MIN 0.0312
#define EDGE_THRESHOLD_MAX 0.125
#define QUALITY(q) ((q) < 5 ? 1.0 : ((q) > 5 ? ((q) < 10 ? 2.0 : ((q) < 11 ? 4.0 : 8.0)) : 1.5))
#define ITERATIONS 12
#define SUBPIXEL_QUALITY 0.75
float rgb2luma(vec3 rgb){
return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114)));
}
/** Performs FXAA post-process anti-aliasing as described in the Nvidia FXAA white paper and the associated shader code.
*/
void main(){
vec4 colorCenter = texture(screenTexture,In.uv);
// Luma at the current fragment
float lumaCenter = rgb2luma(colorCenter.rgb);
// Luma at the four direct neighbours of the current fragment.
float lumaDown = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2( 0,-1)).rgb);
float lumaUp = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2( 0, 1)).rgb);
float lumaLeft = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2(-1, 0)).rgb);
float lumaRight = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2( 1, 0)).rgb);
// Find the maximum and minimum luma around the current fragment.
float lumaMin = min(lumaCenter,min(min(lumaDown,lumaUp),min(lumaLeft,lumaRight)));
float lumaMax = max(lumaCenter,max(max(lumaDown,lumaUp),max(lumaLeft,lumaRight)));
// Compute the delta.
float lumaRange = lumaMax - lumaMin;
// If the luma variation is lower that a threshold (or if we are in a really dark area), we are not on an edge, don't perform any AA.
if(lumaRange < max(EDGE_THRESHOLD_MIN,lumaMax*EDGE_THRESHOLD_MAX)){
fragColor = colorCenter;
return;
}
// Query the 4 remaining corners lumas.
float lumaDownLeft = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2(-1,-1)).rgb);
float lumaUpRight = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2( 1, 1)).rgb);
float lumaUpLeft = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2(-1, 1)).rgb);
float lumaDownRight = rgb2luma(textureLodOffset(screenTexture,In.uv, 0.0,ivec2( 1,-1)).rgb);
// Combine the four edges lumas (using intermediary variables for future computations with the same values).
float lumaDownUp = lumaDown + lumaUp;
float lumaLeftRight = lumaLeft + lumaRight;
// Same for corners
float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
float lumaDownCorners = lumaDownLeft + lumaDownRight;
float lumaRightCorners = lumaDownRight + lumaUpRight;
float lumaUpCorners = lumaUpRight + lumaUpLeft;
// Compute an estimation of the gradient along the horizontal and vertical axis.
float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp ) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
// Is the local edge horizontal or vertical ?
bool isHorizontal = (edgeHorizontal >= edgeVertical);
// Choose the step size (one pixel) accordingly.
float stepLength = isHorizontal ? inverseScreenSize.y : inverseScreenSize.x;
// Select the two neighboring texels lumas in the opposite direction to the local edge.
float luma1 = isHorizontal ? lumaDown : lumaLeft;
float luma2 = isHorizontal ? lumaUp : lumaRight;
// Compute gradients in this direction.
float gradient1 = luma1 - lumaCenter;
float gradient2 = luma2 - lumaCenter;
// Which direction is the steepest ?
bool is1Steepest = abs(gradient1) >= abs(gradient2);
// Gradient in the corresponding direction, normalized.
float gradientScaled = 0.25*max(abs(gradient1),abs(gradient2));
// Average luma in the correct direction.
float lumaLocalAverage = 0.0;
if(is1Steepest){
// Switch the direction
stepLength = - stepLength;
lumaLocalAverage = 0.5*(luma1 + lumaCenter);
} else {
lumaLocalAverage = 0.5*(luma2 + lumaCenter);
}
// Shift UV in the correct direction by half a pixel.
vec2 currentUv = In.uv;
if(isHorizontal){
currentUv.y += stepLength * 0.5;
} else {
currentUv.x += stepLength * 0.5;
}
// Compute offset (for each iteration step) in the right direction.
vec2 offset = isHorizontal ? vec2(inverseScreenSize.x,0.0) : vec2(0.0,inverseScreenSize.y);
// Compute UVs to explore on each side of the edge, orthogonally. The QUALITY allows us to step faster.
vec2 uv1 = currentUv - offset * QUALITY(0);
vec2 uv2 = currentUv + offset * QUALITY(0);
// Read the lumas at both current extremities of the exploration segment, and compute the delta wrt to the local average luma.
float lumaEnd1 = rgb2luma(textureLod(screenTexture,uv1, 0.0).rgb);
float lumaEnd2 = rgb2luma(textureLod(screenTexture,uv2, 0.0).rgb);
lumaEnd1 -= lumaLocalAverage;
lumaEnd2 -= lumaLocalAverage;
// If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge.
bool reached1 = abs(lumaEnd1) >= gradientScaled;
bool reached2 = abs(lumaEnd2) >= gradientScaled;
bool reachedBoth = reached1 && reached2;
// If the side is not reached, we continue to explore in this direction.
if(!reached1){
uv1 -= offset * QUALITY(1);
}
if(!reached2){
uv2 += offset * QUALITY(1);
}
// If both sides have not been reached, continue to explore.
if(!reachedBoth){
for(int i = 2; i < ITERATIONS; i++){
// If needed, read luma in 1st direction, compute delta.
if(!reached1){
lumaEnd1 = rgb2luma(textureLod(screenTexture, uv1, 0.0).rgb);
lumaEnd1 = lumaEnd1 - lumaLocalAverage;
}
// If needed, read luma in opposite direction, compute delta.
if(!reached2){
lumaEnd2 = rgb2luma(textureLod(screenTexture, uv2, 0.0).rgb);
lumaEnd2 = lumaEnd2 - lumaLocalAverage;
}
// If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge.
reached1 = abs(lumaEnd1) >= gradientScaled;
reached2 = abs(lumaEnd2) >= gradientScaled;
reachedBoth = reached1 && reached2;
// If the side is not reached, we continue to explore in this direction, with a variable quality.
if(!reached1){
uv1 -= offset * QUALITY(i);
}
if(!reached2){
uv2 += offset * QUALITY(i);
}
// If both sides have been reached, stop the exploration.
if(reachedBoth){ break;}
}
}
// Compute the distances to each side edge of the edge (!).
float distance1 = isHorizontal ? (In.uv.x - uv1.x) : (In.uv.y - uv1.y);
float distance2 = isHorizontal ? (uv2.x - In.uv.x) : (uv2.y - In.uv.y);
// In which direction is the side of the edge closer ?
bool isDirection1 = distance1 < distance2;
float distanceFinal = min(distance1, distance2);
// Thickness of the edge.
float edgeThickness = (distance1 + distance2);
// Is the luma at center smaller than the local average ?
bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
// If the luma at center is smaller than at its neighbour, the delta luma at each end should be positive (same variation).
bool correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller;
bool correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller;
// Only keep the result in the direction of the closer side of the edge.
bool correctVariation = isDirection1 ? correctVariation1 : correctVariation2;
// UV offset: read in the direction of the closest side of the edge.
float pixelOffset = - distanceFinal / edgeThickness + 0.5;
// If the luma variation is incorrect, do not offset.
float finalOffset = correctVariation ? pixelOffset : 0.0;
// Sub-pixel shifting
// Full weighted average of the luma over the 3x3 neighborhood.
float lumaAverage = (1.0/12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
// Ratio of the delta between the global average and the center luma, over the luma range in the 3x3 neighborhood.
float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter)/lumaRange,0.0,1.0);
float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
// Compute a sub-pixel offset based on this delta.
float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY;
// Pick the biggest of the two offsets.
finalOffset = max(finalOffset,subPixelOffsetFinal);
// Compute the final UV coordinates.
vec2 finalUv = In.uv;
if(isHorizontal){
finalUv.y += finalOffset * stepLength;
} else {
finalUv.x += finalOffset * stepLength;
}
// Read the color at the new UV coordinates, and use it.
vec4 finalColor = textureLod(screenTexture,finalUv, 0.0);
fragColor = finalColor;
}