1use gl_context_loader::{GLint, GLuint};
11
12#[derive(Debug, Clone)]
14pub struct FxaaConfig {
15 pub enabled: bool,
17 pub edge_threshold: f32,
20 pub edge_threshold_min: f32,
23}
24
25impl Default for FxaaConfig {
26 fn default() -> Self {
27 Self {
28 enabled: false, edge_threshold: 0.125,
30 edge_threshold_min: 0.0312,
31 }
32 }
33}
34
35impl FxaaConfig {
36 pub fn enabled() -> Self {
38 Self {
39 enabled: true,
40 ..Default::default()
41 }
42 }
43
44 pub fn high_quality() -> Self {
46 Self {
47 enabled: true,
48 edge_threshold: 0.063,
49 edge_threshold_min: 0.0312,
50 }
51 }
52
53 pub fn balanced() -> Self {
55 Self {
56 enabled: true,
57 edge_threshold: 0.125,
58 edge_threshold_min: 0.0312,
59 }
60 }
61
62 pub fn performance() -> Self {
64 Self {
65 enabled: true,
66 edge_threshold: 0.25,
67 edge_threshold_min: 0.0625,
68 }
69 }
70}
71
72pub static FXAA_VERTEX_SHADER: &[u8] = b"#version 150
74
75#if __VERSION__ != 100
76 #define varying out
77 #define attribute in
78#endif
79
80attribute vec2 vAttrXY;
81varying vec2 vTexCoord;
82
83void main() {
84 vTexCoord = vAttrXY * 0.5 + 0.5; // Convert from [-1,1] to [0,1]
85 gl_Position = vec4(vAttrXY, 0.0, 1.0);
86}
87";
88
89pub static FXAA_FRAGMENT_SHADER: &[u8] = b"#version 150
91
92precision highp float;
93
94#if __VERSION__ == 100
95 #define oFragColor gl_FragColor
96 #define texture texture2D
97#else
98 out vec4 oFragColor;
99#endif
100
101#if __VERSION__ != 100
102 #define varying in
103#endif
104
105uniform sampler2D uTexture;
106uniform vec2 uTexelSize; // 1.0 / texture dimensions
107uniform float uEdgeThreshold;
108uniform float uEdgeThresholdMin;
109
110varying vec2 vTexCoord;
111
112// Luminance conversion (Rec. 709)
113float luminance(vec3 color) {
114 return dot(color, vec3(0.2126, 0.7152, 0.0722));
115}
116
117void main() {
118 // Sample center and 4-neighborhood
119 vec3 colorCenter = texture(uTexture, vTexCoord).rgb;
120 vec3 colorN = texture(uTexture, vTexCoord + vec2(0.0, -uTexelSize.y)).rgb;
121 vec3 colorS = texture(uTexture, vTexCoord + vec2(0.0, uTexelSize.y)).rgb;
122 vec3 colorE = texture(uTexture, vTexCoord + vec2(uTexelSize.x, 0.0)).rgb;
123 vec3 colorW = texture(uTexture, vTexCoord + vec2(-uTexelSize.x, 0.0)).rgb;
124
125 // Calculate luminance
126 float lumCenter = luminance(colorCenter);
127 float lumN = luminance(colorN);
128 float lumS = luminance(colorS);
129 float lumE = luminance(colorE);
130 float lumW = luminance(colorW);
131
132 // Find min/max luminance in neighborhood
133 float lumMin = min(lumCenter, min(min(lumN, lumS), min(lumE, lumW)));
134 float lumMax = max(lumCenter, max(max(lumN, lumS), max(lumE, lumW)));
135 float lumRange = lumMax - lumMin;
136
137 // Early exit if no edge detected
138 if (lumRange < max(uEdgeThresholdMin, lumMax * uEdgeThreshold)) {
139 oFragColor = vec4(colorCenter, 1.0);
140 return;
141 }
142
143 // Calculate edge direction
144 float lumNS = lumN + lumS;
145 float lumEW = lumE + lumW;
146
147 vec2 dir;
148 dir.x = lumNS - lumEW;
149 dir.y = lumN - lumS;
150
151 // Normalize edge direction
152 float dirReduce = max((lumN + lumS + lumE + lumW) * 0.25 * 0.25, 0.0078125);
153 float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
154 dir = min(vec2(8.0), max(vec2(-8.0), dir * rcpDirMin)) * uTexelSize;
155
156 // Sample along edge direction
157 vec3 color1 = 0.5 * (
158 texture(uTexture, vTexCoord + dir * (1.0/3.0 - 0.5)).rgb +
159 texture(uTexture, vTexCoord + dir * (2.0/3.0 - 0.5)).rgb
160 );
161
162 vec3 color2 = color1 * 0.5 + 0.25 * (
163 texture(uTexture, vTexCoord + dir * -0.5).rgb +
164 texture(uTexture, vTexCoord + dir * 0.5).rgb
165 );
166
167 float lum2 = luminance(color2);
168
169 // Choose appropriate sample based on luminance range
170 if (lum2 < lumMin || lum2 > lumMax) {
171 oFragColor = vec4(color1, 1.0);
172 } else {
173 oFragColor = vec4(color2, 1.0);
174 }
175}
176";