-
-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathNFAA.fx
More file actions
357 lines (313 loc) · 15.1 KB
/
Copy pathNFAA.fx
File metadata and controls
357 lines (313 loc) · 15.1 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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
////-------------//
///**NFAA Fast**///
//-------------////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//* Normal Filter Anti Aliasing.
//* For ReShade 3.0+ & Freestyle
//* ---------------------------------
//* NFAA
//* Due Diligence
//* Based on port by b34r
//* https://www.gamedev.net/forums/topic/580517-nfaa---a-post-process-anti-aliasing-filter-results-implementation-details/?page=2
//* Later rewritten by Eric B. AKA Kourinn
//* https://github.com/BlueSkyDefender/AstrayFX/pull/17
//* If I missed any please tell me.
//*
//* LICENSE
//* ============
//* Normal Filter Anti Aliasing is licenses under: Attribution-NoDerivatives 4.0 International
//*
//* You are free to:
//* Share - copy and redistribute the material in any medium or format
//* for any purpose, even commercially.
//* The licensor cannot revoke these freedoms as long as you follow the license terms.
//* Under the following terms:
//* Attribution - You must give appropriate credit, provide a link to the license, and indicate if changes were made.
//* You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
//*
//* NoDerivatives - If you remix, transform, or build upon the material, you may not distribute the modified material.
//*
//* No additional restrictions - You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
//*
//* https://creativecommons.org/licenses/by-nd/4.0/
//*
//* Have fun,
//* Jose Negrete AKA BlueSkyDefender
//*
//* https://github.com/BlueSkyDefender/Depth3D
//*
//* Have fun,
//* Jose Negrete AKA BlueSkyDefender
//*
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define BUFFER_PIXEL_SIZE float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT)
#define BUFFER_ASPECT_RATIO (BUFFER_WIDTH * BUFFER_RCP_HEIGHT)
uniform int EdgeDetectionType <
ui_type = "combo";
ui_items = "Luminance edge detection\0Perceived Luminance edge detection\0Color edge detection\0Perceived Color edge detection\0";
ui_label = "Edge Detection Type";
> = 3;
uniform float EdgeDetectionThreshold <
ui_type = "drag";
ui_label = "Edge Detection Threshold";
ui_tooltip = "The difference in Luminence/Color that would be perceived as an edge.\n"
"Try lowering this slightly if the Edge Mask misses some edges.\n"
"Default is 0.063";
ui_min = 0.050; ui_max = 0.200; ui_step = 0.001;
> = 0.100;
uniform float EdgeSearchRadius <
ui_type = "drag";
ui_label = "Edge Search Radius";
ui_tooltip = "The radius to search for edges.\n"
"Try raising this if using in-game upscaling.\n"
"Default is 1.000";
ui_min = 0.000; ui_max = 4.000; ui_step = 0.001;
> = 1.000;
uniform float UnblurFilterStrength <
ui_type = "drag";
ui_label = "Unblur Filter Strength";
ui_tooltip = "Adjusts the Edge Mask and Corner Mask contrast for filtering unwanted edge blur.\n"
"Try raising this if text or icons become blurry.\n"
"Try lowering this if edges are still too aliased.\n"
"Default is 1.000";
ui_min = 0.000; ui_max = 2.000; ui_step = 0.001;
> = 1.000;
uniform float BlurStrength <
ui_type = "drag";
ui_label = "Blur Strength";
ui_tooltip = "Adjusts the Normal Map weights for stronger edge blur.\n"
"Try raising this if edges are still too aliased.\n"
"Try lowering this if text or icons become blurry.\n"
"Default is 1.000";
ui_min = 0.000; ui_max = 2.000; ui_step = 0.001;
> = 1.000;
uniform float2 BlurSize <
ui_type = "drag";
ui_label = "Blur Size";
ui_tooltip = "Adjusts the Normal Map depth for larger/longer edge blur.\n"
"Inputs are blur size parallel and perpendicular to the edge respectively.\n"
"Try raising this if edges are still too aliased.\n"
"Try lowering this if text or icons become blurry.\n"
"Defaults are 2.000 and 1.000";
ui_min = 0.000; ui_max = 4.000; ui_step = 0.001;
> = float2(2.000, 1.000);
uniform int DebugOutput <
ui_type = "combo";
ui_label = "Debug Output";
ui_items = "None\0Edge Mask View\0Corner Mask View\0Normal Map View\0Pre-Blur Mask View\0Post-Blur Mask View\0";
ui_tooltip = "Edge Mask View shows the Edge Detection and Unblur Filter Strength.\n"
"Corner Mask View shows the Corner Detection and Unblur Filter Strength.\n"
"Normal Map View shows the Normal Map depth, used for Blur Size and Blur Direction.\n"
"Pre-Blur Mask View shows just the edges that will be blurred.\n"
"Post-Blur Mask View shows just the edges have been blurred.";
ui_spacing = 2;
> = 0;
////////////////////////////////////////////////////////////Variables////////////////////////////////////////////////////////////////////
// sRGB Luminance
static const float3 LinearizeVector[4] = { float3(0.2126, 0.7152, 0.0722), float3(0.299, 0.587, 0.114), float3(0.3333333, 0.3333333, 0.3333333), float3(0.299, 0.587, 0.114) };
static const float Cos45 = 0.70710678118654752440084436210485;
static const float MaxSlope = 1024.0;
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
////////////////////////////////////////////////////////////Functions////////////////////////////////////////////////////////////////////
float LinearDifference(float3 A, float3 B)
{
float lumDiff = dot(A, LinearizeVector[EdgeDetectionType]) - dot(B, LinearizeVector[EdgeDetectionType]);
if (EdgeDetectionType < 2)
return lumDiff;
float3 C = abs(A - B);
return max(max(C.r, C.g), C.b) * (lumDiff < 0.0 ? -1.0 : 1.0); // sign intrinsic can return 0, which we don't want. Plus this is faster.
}
float2 Rotate45(float2 p) {
return float2(mad(p.x, Cos45, -p.y * Cos45), mad(p.x, Cos45, p.y * Cos45));
// return float2(p.x * Cos45 - p.y * Cos45, p.x * Cos45 + p.y * Cos45);
}
////////////////////////////////////////////////////////////NFAA////////////////////////////////////////////////////////////////////
float4 NFAA(float2 texcoord, float4 offsets[4])
{
float4 color = tex2Dlod(BackBuffer, float4(texcoord, 0.0, 0.0));
// Find Edges
// +---+---+---+---+---+
// | | | | | |
// +---+---+---+---+---+
// | | e | f | g | |
// +---+--(a)-(b)--+---+
// | | h | P | i | |
// +---+--(c)-(d)--+---+
// | | j | k | l | |
// +---+---+---+---+---+
// | | | | | |
// +---+---+---+---+---+
// Much better at horizontal/vertical lines, slightly better diagonals, always compares 6 pixels, not 2.
float3 a = tex2Dlod(BackBuffer, offsets[0]).rgb;
float3 b = tex2Dlod(BackBuffer, offsets[1]).rgb;
float3 c = tex2Dlod(BackBuffer, offsets[2]).rgb;
float3 d = tex2Dlod(BackBuffer, offsets[3]).rgb;
// Original edge detection from b34r & BlueSkyDefender
// +---+---+---+---+---+
// | | | | | |
// +---+---+---+---+---+
// | | | t | | |
// +---+---+---+---+---+
// | | l | C | r | |
// +---+---+---+---+---+
// | | | b | | |
// +---+---+---+---+---+
// | | | | | |
// +---+---+---+---+---+
// float angle = 0.0;
// float3 t = tex2Dlod(BackBuffer, float4(mad(float2(0.0, -EdgeSearchRadius), BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
// float3 b = tex2Dlod(BackBuffer, float4(mad(float2(-0.0, EdgeSearchRadius), BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
// float3 r = tex2Dlod(BackBuffer, float4(mad(float2(EdgeSearchRadius, 0.0), BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
// float3 l = tex2Dlod(BackBuffer, float4(mad(float2(-EdgeSearchRadius, 0.0), BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
// float2 n = float2(LinearDifference(t, b), LinearDifference(r, l));
// i.e. top vs bottom = a + b - (c + d) = (e + 2*f + g) / 4 - (j + 2*k + l) / 4
float2 normal = float2(LinearDifference(b + d, a + c), LinearDifference(c + d, a + b)); // right - left, bottom - top
float edge = length(normal);
float edgeMask = 1.0;
float cornerMask = 1.0;
if (edge > EdgeDetectionThreshold)
{
// Lets make that edgeMask for a sharper image.
float edgeConfidence = log2(edge / EdgeDetectionThreshold);
edgeMask = saturate(mad(edgeConfidence, UnblurFilterStrength - 2.0, 1.0));
// edgeMask = saturate((1.0 - (UnblurFilterStrength - 2.0) * edgeConfidence);
// Then subtract corners from edge mask to avoid bluring text and detailed icons
float4 corners = float4(LinearDifference(a + b + c, 3.0 * d), LinearDifference(a + b + d, 3.0 * c), LinearDifference(a + c + d, 3.0 * b), LinearDifference(c + d + b, 3.0 * a));
float corner = dot(abs(corners), 0.25);
cornerMask = saturate(edgeMask + corner);
// calculate x/y coordinates along the edge at specified distances and offsets
// +---+---+---+---+---+ +---+---+---+---+---+
// | | | | | | | | | | | |
// +---+---+---+---+---+ +---+---+---+---+---+
// | | | | | | | | | (e) | |
// +---+(g)a---b(e)+---+ +---+---a---b(f)+---+
// | |-O | P | O | | | | | P | | |
// +---+(h)c---d(f)+---+ +---+(g)c---d---+---+
// | | | | | | | | (h) | | |
// +---+---+---+---+---+ +---+---+---+---+---+
// | | | | | | | | | | | |
// +---+---+---+---+---+ +---+---+---+---+---+
// slope m = normal.r / normal.g
// distance d
// y = mx
// d^2 = y^2 + x^2 = (mx)^2+x^2
// d^2 = x^2(1 + m^2)
// x^2 = d^2/(1 + m^2)
// Follow the edge for 1/2 of BlurSize.x
float4 offset;
float m = normal.g != 0 ? normal.r / normal.g : MaxSlope;
float d = 0.5 * BlurSize.x;
offset.x = sqrt(d *d / (1.0 + m * m));
offset.y = m * offset.x;
// Then move perpendicular to the edge for 1/2 of BlurSize.y
m = normal.r != 0 ? -normal.g / normal.r : MaxSlope;
d = 0.5 * BlurSize.y;
offset.z = sqrt(d * d / (1.0 + m * m));
offset.w = m * offset.z;
float3 e = tex2Dlod(BackBuffer, float4(mad(offset.xy + offset.zw, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
float3 f = tex2Dlod(BackBuffer, float4(mad(offset.xy - offset.zw, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
float3 g = tex2Dlod(BackBuffer, float4(mad(-offset.xy - offset.zw, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
float3 h = tex2Dlod(BackBuffer, float4(mad(-offset.xy + offset.zw, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0)).rgb;
// It's possible to reduce taps by re-using edge detection taps a, b, c, d,
// but it's not worth it. Nearby pixels should already be cached, and it would need more math.
// apply blur
if (DebugOutput != 4)
color.rgb = lerp(color.rgb, (e + f + g + h) * 0.25, BlurStrength * 0.5 * (1.0 - cornerMask));
// Pre/Post Blur Mask
if (DebugOutput > 3)
color.a = cornerMask;
// original blur from b34r & BlueSkyDefender
// may need some work to be functional again, due to some variable name refactoring after I reversed how/why it worked
// +---+---+---+---+---+
// | | | | | |
// +---+---+---+---+---+
// |\\\| | x | x | |
// +---+---+---+---+---+
// | |\\\|\\\| x | |
// +---+---+---+---+---+
// | | | x |\\\|\\\|
// +---+---+---+---+---+
// | | | | | |
// +---+---+---+---+---+
// y = -0.5x; n.x = 1; n.y = 0.5; nl = 1.18; dn.x = 0.85; dn.y = 0.42;
// t0/1 = 0.425, 0.21; d ~= 0.5
// t2/3 = 0.765, -0.38; d ~= 0.85
// float2 dn = n / nl * BlurSize;
// float4 t0 = tex2Dlod(BackBuffer, float4(mad(-dn * 0.5, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0));
// float4 t1 = tex2Dlod(BackBuffer, float4(mad(dn * 0.5, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0));
// float4 t2 = tex2Dlod(BackBuffer, float4(mad(float2(dn.x, -dn.y) * 0.9, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0));
// float4 t3 = tex2Dlod(BackBuffer, float4(mad(float2(-dn.x, dn.y) * 0.9, BUFFER_PIXEL_SIZE, texcoord), 0.0, 0.0));
// color = lerp(mad(color, 0.23, 0.175 * (t2 + t3) + 0.21 * (t0 + t1)), color, edgeMask);
}
else {
color.a = 0.0;
}
if(DebugOutput == 1) // Edge Mask
{
color.rgb = edgeMask;
}
if(DebugOutput == 2) // Corner Mask
{
color.rgb = cornerMask;
}
else if (DebugOutput == 3) // Normal Map
{
// Normal map, right = red, green = top, configured using white box with black background
float3 normalMap;
normalMap.b = 1.0;
normalMap.rg = mad(float2(-normal.r, normal.g) * BlurSize.x * BlurStrength, 0.25, 0.5);
normalMap.rg = lerp(float2(0.5, 0.5), saturate(normalMap.rg), (1.0 - edgeMask) * BlurSize.y);
color.rgb = normalMap;
}
else if (DebugOutput > 3) { // Pre/Post Blur Mask
uint row = floor(texcoord.y / 0.1);
uint col = floor(texcoord.x * BUFFER_ASPECT_RATIO / 0.1);
float3 lightGray = 0.5;
float3 darkGray = 0.1667;
lightGray = lerp(lightGray, color.rgb, color.a);
darkGray = lerp(darkGray, color.rgb, color.a);
// Create checkerboad background to depict transparency
if (row % 2 == 0) {
if (col % 2 == 0) {
color.rgb = lightGray;
}
else {
color.rgb = darkGray;
}
}
else {
if (col % 2 == 0) {
color.rgb = darkGray;
}
else {
color.rgb = lightGray;
}
}
}
color.a = 1.0;
return color;
}
void NFAA_VS(in uint id : SV_VertexID, out float4 position : SV_POSITION, out float2 texcoord : TEXCOORD, out float4 offsets[4] : TEXCOORD1 )
{
texcoord.x = (id == 2) ? 2.0 : 0.0;
texcoord.y = (id == 1) ? 2.0 : 0.0;
position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
float2 offset = Cos45 * EdgeSearchRadius * BUFFER_PIXEL_SIZE;
offsets[0] = float4(mad(float2(-1.0, -1.0), offset, texcoord), 0.0, 0.0);
offsets[1] = float4(mad(float2(1.0, -1.0), offset, texcoord), 0.0, 0.0);
offsets[2] = float4(mad(float2(-1.0, 1.0), offset, texcoord), 0.0, 0.0);
offsets[3] = float4(mad(float2(1.0, 1.0), offset, texcoord), 0.0, 0.0);
}
float4 NFAA_PS(in float4 position : SV_Position, in float2 texcoord : TEXCOORD, in float4 offsets[4] : TEXCOORD1) : SV_Target
{
return NFAA(texcoord, offsets);
}
technique NFAA
{
pass NFAA_Fast
{
VertexShader = NFAA_VS;
PixelShader = NFAA_PS;
}
}