Skip to content

Commit d1fbcde

Browse files
Implement FXAA
1 parent 2659a97 commit d1fbcde

File tree

2 files changed

+166
-3
lines changed

2 files changed

+166
-3
lines changed

Assets/FXAA.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55
public class FXAA : MonoBehaviour {
66
public Shader fxaaShader;
77

8+
[Range(0.0312f, 0.0833f)]
9+
public float contrastThreshold = 0.0312f;
10+
11+
[Range(0.063f, 0.333f)]
12+
public float relativeThreshold = 0.063f;
13+
14+
[Range(0.0f, 1.0f)]
15+
public float subpixelBlending = 1.0f;
16+
817
private Material fxaaMaterial;
918

1019
void OnEnable() {
@@ -13,7 +22,18 @@ void OnEnable() {
1322
}
1423

1524
void OnRenderImage(RenderTexture source, RenderTexture destination) {
16-
Graphics.Blit(source, destination, fxaaMaterial);
25+
var luminanceTex = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.RHalf);
26+
27+
fxaaMaterial.SetFloat("_ContrastThreshold", contrastThreshold);
28+
fxaaMaterial.SetFloat("_RelativeThreshold", relativeThreshold);
29+
fxaaMaterial.SetFloat("_SubpixelBlending", subpixelBlending);
30+
31+
Graphics.Blit(source, luminanceTex, fxaaMaterial, 0);
32+
33+
fxaaMaterial.SetTexture("_LuminanceTex", luminanceTex);
34+
35+
Graphics.Blit(source, destination, fxaaMaterial, 1);
36+
RenderTexture.ReleaseTemporary(luminanceTex);
1737
}
1838

1939
void OnDisable() {

Assets/FXAA.shader

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,163 @@ Shader "Hidden/FXAA" {
2626
return o;
2727
}
2828

29-
sampler2D _MainTex;
29+
sampler2D _MainTex, _LuminanceTex;
3030
float4 _MainTex_TexelSize;
3131

32+
float _ContrastThreshold, _RelativeThreshold, _SubpixelBlending;
33+
3234
ENDCG
3335

36+
// Luminance Pass
37+
Pass {
38+
CGPROGRAM
39+
#pragma vertex vp
40+
#pragma fragment fp
41+
42+
float4 fp(v2f i) : SV_Target {
43+
return LinearRgbToLuminance(saturate(tex2D(_MainTex, i.uv).rgb));
44+
}
45+
ENDCG
46+
}
47+
48+
// Luminance Pass
3449
Pass {
3550
CGPROGRAM
3651
#pragma vertex vp
3752
#pragma fragment fp
3853

54+
#define EDGE_STEP_COUNT 10
55+
#define EDGE_STEPS 1, 1.5, 2, 2, 2, 2, 2, 2, 2, 4
56+
#define EDGE_GUESS 8
57+
58+
static const float edgeSteps[EDGE_STEP_COUNT] = { EDGE_STEPS };
59+
3960
float4 fp(v2f i) : SV_Target {
4061
float4 col = tex2D(_MainTex, i.uv);
4162

42-
return col;
63+
// Luminance Neighborhood
64+
float m = tex2D(_LuminanceTex, i.uv + float2(0, 0) * _MainTex_TexelSize.xy);
65+
66+
float n = tex2D(_LuminanceTex, i.uv + float2(0, 1) * _MainTex_TexelSize.xy);
67+
float e = tex2D(_LuminanceTex, i.uv + float2(1, 0) * _MainTex_TexelSize.xy);
68+
float s = tex2D(_LuminanceTex, i.uv + float2(0, -1) * _MainTex_TexelSize.xy);
69+
float w = tex2D(_LuminanceTex, i.uv + float2(-1, 0) * _MainTex_TexelSize.xy);
70+
71+
float ne = tex2D(_LuminanceTex, i.uv + float2(1, 1) * _MainTex_TexelSize.xy);
72+
float nw = tex2D(_LuminanceTex, i.uv + float2(-1, 1) * _MainTex_TexelSize.xy);
73+
float se = tex2D(_LuminanceTex, i.uv + float2(1, -1) * _MainTex_TexelSize.xy);
74+
float sw = tex2D(_LuminanceTex, i.uv + float2(-1, -1) * _MainTex_TexelSize.xy);
75+
76+
// Apply Thresholding From Cardinals
77+
float maxL = max(max(max(max(m, n), e), s), w);
78+
float minL = min(min(min(min(m, n), e), s), w);
79+
float contrast = maxL - minL;
80+
81+
if (contrast < max(_ContrastThreshold, _RelativeThreshold * maxL)) return col;
82+
83+
// Determine Blend Factor
84+
float filter = 2 * (n + e + s + w) + ne + nw + se + sw;
85+
filter *= 1.0f / 12.0f;
86+
filter = abs(filter - m);
87+
filter = saturate(filter / contrast);
88+
89+
float blendFactor = smoothstep(0, 1, filter);
90+
blendFactor *= blendFactor * _SubpixelBlending;
91+
92+
// Edge Prediction
93+
float horizontal = abs(n + s - 2 * m) * 2 + abs(ne + se - 2 * e) + abs(nw + sw - 2 * w);
94+
float vertical = abs(e + w - 2 * m) * 2 + abs(ne + nw - 2 * n) + abs(se + sw - 2 * s);
95+
bool isHorizontal = horizontal >= vertical;
96+
97+
float pLuminance = isHorizontal ? n : e;
98+
float nLuminance = isHorizontal ? s : w;
99+
float pGradient = abs(pLuminance - m);
100+
float nGradient = abs(nLuminance - m);
101+
102+
float pixelStep = isHorizontal ? _MainTex_TexelSize.y : _MainTex_TexelSize.x;
103+
104+
float oppositeLuminance = pLuminance;
105+
float gradient = pGradient;
106+
107+
if (pGradient < nGradient) {
108+
pixelStep = -pixelStep;
109+
oppositeLuminance = nLuminance;
110+
gradient = nGradient;
111+
}
112+
113+
float2 uvEdge = i.uv;
114+
float2 edgeStep;
115+
if (isHorizontal) {
116+
uvEdge.y += pixelStep * 0.5f;
117+
edgeStep = float2(_MainTex_TexelSize.x, 0);
118+
} else {
119+
uvEdge.x += pixelStep * 0.5f;
120+
edgeStep = float2(0, _MainTex_TexelSize.y);
121+
}
122+
123+
float edgeLuminance = (m + oppositeLuminance) * 0.5f;
124+
float gradientThreshold = gradient * 0.25f;
125+
126+
float2 puv = uvEdge + edgeStep * edgeSteps[0];
127+
float pLuminanceDelta = tex2D(_LuminanceTex, puv) - edgeLuminance;
128+
bool pAtEnd = abs(pLuminanceDelta) >= gradientThreshold;
129+
130+
UNITY_UNROLL
131+
for (int j = 1; j < EDGE_STEP_COUNT && !pAtEnd; ++j) {
132+
puv += edgeStep * edgeSteps[j];
133+
pLuminanceDelta = tex2D(_LuminanceTex, puv) - edgeLuminance;
134+
pAtEnd = abs(pLuminanceDelta) >= gradientThreshold;
135+
}
136+
137+
if (!pAtEnd)
138+
puv += edgeStep * EDGE_GUESS;
139+
140+
float2 nuv = uvEdge - edgeStep * edgeSteps[0];
141+
float nLuminanceDelta = tex2D(_LuminanceTex, nuv) - edgeLuminance;
142+
bool nAtEnd = abs(nLuminanceDelta) >= gradientThreshold;
143+
144+
UNITY_UNROLL
145+
for (int k = 1; k < EDGE_STEP_COUNT && !nAtEnd; ++k) {
146+
nuv -= edgeStep * edgeSteps[k];
147+
nLuminanceDelta = tex2D(_LuminanceTex, nuv) - edgeLuminance;
148+
nAtEnd = abs(nLuminanceDelta) >= gradientThreshold;
149+
}
150+
151+
if (!nAtEnd)
152+
nuv -= edgeStep * EDGE_GUESS;
153+
154+
155+
float pDistance, nDistance;
156+
if (isHorizontal) {
157+
pDistance = puv.x - i.uv.x;
158+
nDistance = i.uv.x - nuv.x;
159+
} else {
160+
pDistance = puv.y - i.uv.y;
161+
nDistance = i.uv.y - nuv.y;
162+
}
163+
164+
float shortestDistance = nDistance;
165+
bool deltaSign = nLuminanceDelta >= 0;
166+
167+
if (pDistance <= nDistance) {
168+
shortestDistance = pDistance;
169+
deltaSign = pLuminanceDelta >= 0;
170+
}
171+
172+
if (deltaSign == (m - edgeLuminance >= 0)) return col;
173+
174+
float edgeBlendFactor = 0.5f - shortestDistance / (pDistance + nDistance);
175+
176+
float finalBlendFactor = max(blendFactor, edgeBlendFactor);
177+
178+
float2 uv = i.uv;
179+
180+
if (isHorizontal)
181+
uv.y += pixelStep * finalBlendFactor;
182+
else
183+
uv.x += pixelStep * finalBlendFactor;
184+
185+
return tex2Dlod(_MainTex, float4(uv, 0, 0));
43186
}
44187
ENDCG
45188
}

0 commit comments

Comments
 (0)