1 <?xml version="1.0" encoding="UTF-8"?>
5 Copyright (C) 2012 Joshua Street
6 Copyright (C) 2011, 2012 Hyllian/Jararaca - sergiogdb@gmail.com
7 Copyright (C) 2012 crazy46guy (GLSL conversion)
9 Portions of this algorithm were taken from Hyllian's 5xBR v3.7c
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 <shader language="GLSL">
29 uniform vec2 rubyTextureSize;
32 varying vec4 xyp_1_2_3;
33 varying vec4 xyp_5_10_15;
34 varying vec4 xyp_6_7_8;
35 varying vec4 xyp_9_14_9;
36 varying vec4 xyp_11_12_13;
37 varying vec4 xyp_16_17_18;
38 varying vec4 xyp_21_22_23;
41 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
43 float x = 1.0 / rubyTextureSize.x;
44 float y = 1.0 / rubyTextureSize.y;
50 +-----+-----+-----+-----+-----+
52 +-----+-----+-----+-----+-----+
54 +-----+-----+-----+-----+-----+
55 | 10 | 11 | 12 | 13 | 14 |
56 +-----+-----+-----+-----+-----+
57 | 15 | 16 | 17 | 18 | 19 |
58 +-----+-----+-----+-----+-----+
60 +-----+-----+-----+-----+-----+
63 tc = gl_MultiTexCoord0.xy;
65 xyp_1_2_3 = tc.xxxy + vec4( -x, 0.0, x, -2.0 * y);
66 xyp_6_7_8 = tc.xxxy + vec4( -x, 0.0, x, -y);
67 xyp_11_12_13 = tc.xxxy + vec4( -x, 0.0, x, 0.0);
68 xyp_16_17_18 = tc.xxxy + vec4( -x, 0.0, x, y);
69 xyp_21_22_23 = tc.xxxy + vec4( -x, 0.0, x, 2.0 * y);
70 xyp_5_10_15 = tc.xyyy + vec4(-2.0 * x, -y, 0.0, y);
71 xyp_9_14_9 = tc.xyyy + vec4( 2.0 * x, -y, 0.0, y);
74 <fragment filter="nearest"><![CDATA[
77 - rubyTexture: texture sampler
78 - rubyTextureSize: size of the texture before rendering
80 uniform sampler2D rubyTexture;
81 uniform vec2 rubyTextureSize;
85 - tc: coordinate of the texel being processed
86 - xyp_[]_[]_[]: a packed coordinate for 3 areas within the texture
89 varying vec4 xyp_1_2_3;
90 varying vec4 xyp_5_10_15;
91 varying vec4 xyp_6_7_8;
92 varying vec4 xyp_9_14_9;
93 varying vec4 xyp_11_12_13;
94 varying vec4 xyp_16_17_18;
95 varying vec4 xyp_21_22_23;
101 Inequation coefficients for interpolation
102 Equations are in the form: Ay + Bx = C
103 45, 30, and 60 denote the angle from x each line the cooeficient variable set builds
105 const vec4 Ai = vec4( 1.0, -1.0, -1.0, 1.0);
106 const vec4 B45 = vec4( 1.0, 1.0, -1.0, -1.0);
107 const vec4 C45 = vec4( 1.5, 0.5, -0.5, 0.5);
108 const vec4 B30 = vec4( 0.5, 2.0, -0.5, -2.0);
109 const vec4 C30 = vec4( 1.0, 1.0, -0.5, 0.0);
110 const vec4 B60 = vec4( 2.0, 0.5, -2.0, -0.5);
111 const vec4 C60 = vec4( 2.0, 0.0, -1.0, 0.5);
113 const vec4 M45 = vec4(0.4, 0.4, 0.4, 0.4);
114 const vec4 M30 = vec4(0.2, 0.4, 0.2, 0.4);
115 const vec4 M60 = M30.yxwz;
116 onst vec4 Mshift = vec4(0.2);
118 // Coefficient for weighted edge detection
119 const float coef = 2.0;
120 // Threshold for if luminance values are "equal"
121 const vec4 threshold = vec4(0.32);
123 // Conversion from RGB to Luminance (from GIMP)
124 const vec3 lum = vec3(0.21, 0.72, 0.07);
126 // Performs same logic operation as && for vectors
127 bvec4 _and_(bvec4 A, bvec4 B) {
128 return bvec4(A.x && B.x, A.y && B.y, A.z && B.z, A.w && B.w);
131 // Performs same logic operation as || for vectors
132 bvec4 _or_(bvec4 A, bvec4 B) {
133 return bvec4(A.x || B.x, A.y || B.y, A.z || B.z, A.w || B.w);
136 // Converts 4 3-color vectors into 1 4-value luminance vector
137 vec4 lum_to(vec3 v0, vec3 v1, vec3 v2, vec3 v3) {
138 return vec4(dot(lum, v0), dot(lum, v1), dot(lum, v2), dot(lum, v3));
141 // Gets the difference between 2 4-value luminance vectors
142 vec4 lum_df(vec4 A, vec4 B) {
146 // Determines if 2 4-value luminance vectors are "equal" based on threshold
147 bvec4 lum_eq(vec4 A, vec4 B) {
148 return lessThan(lum_df(A, B), threshold);
151 vec4 lum_wd(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, vec4 h) {
152 return lum_df(a, b) + lum_df(a, c) + lum_df(d, e) + lum_df(d, f) + 4.0 * lum_df(g, h);
155 // Gets the difference between 2 3-value rgb colors
156 float c_df(vec3 c1, vec3 c2) {
157 vec3 df = abs(c1 - c2);
158 return df.r + df.g + df.b;
164 +-----+-----+-----+-----+-----+
166 +-----+-----+-----+-----+-----+
167 | 5 | 6 | 7 | 8 | 9 |
168 +-----+-----+-----+-----+-----+
169 | 10 | 11 | 12 | 13 | 14 |
170 +-----+-----+-----+-----+-----+
171 | 15 | 16 | 17 | 18 | 19 |
172 +-----+-----+-----+-----+-----+
174 +-----+-----+-----+-----+-----+
176 // Get mask values by performing texture lookup with the uniform sampler
177 vec3 P1 = texture2D(rubyTexture, xyp_1_2_3.xw ).rgb;
178 vec3 P2 = texture2D(rubyTexture, xyp_1_2_3.yw ).rgb;
179 vec3 P3 = texture2D(rubyTexture, xyp_1_2_3.zw ).rgb;
181 vec3 P6 = texture2D(rubyTexture, xyp_6_7_8.xw ).rgb;
182 vec3 P7 = texture2D(rubyTexture, xyp_6_7_8.yw ).rgb;
183 vec3 P8 = texture2D(rubyTexture, xyp_6_7_8.zw ).rgb;
185 vec3 P11 = texture2D(rubyTexture, xyp_11_12_13.xw).rgb;
186 vec3 P12 = texture2D(rubyTexture, xyp_11_12_13.yw).rgb;
187 vec3 P13 = texture2D(rubyTexture, xyp_11_12_13.zw).rgb;
189 vec3 P16 = texture2D(rubyTexture, xyp_16_17_18.xw).rgb;
190 vec3 P17 = texture2D(rubyTexture, xyp_16_17_18.yw).rgb;
191 vec3 P18 = texture2D(rubyTexture, xyp_16_17_18.zw).rgb;
193 vec3 P21 = texture2D(rubyTexture, xyp_21_22_23.xw).rgb;
194 vec3 P22 = texture2D(rubyTexture, xyp_21_22_23.yw).rgb;
195 vec3 P23 = texture2D(rubyTexture, xyp_21_22_23.zw).rgb;
197 vec3 P5 = texture2D(rubyTexture, xyp_5_10_15.xy ).rgb;
198 vec3 P10 = texture2D(rubyTexture, xyp_5_10_15.xz ).rgb;
199 vec3 P15 = texture2D(rubyTexture, xyp_5_10_15.xw ).rgb;
201 vec3 P9 = texture2D(rubyTexture, xyp_9_14_9.xy ).rgb;
202 vec3 P14 = texture2D(rubyTexture, xyp_9_14_9.xz ).rgb;
203 vec3 P19 = texture2D(rubyTexture, xyp_9_14_9.xw ).rgb;
205 // Store luminance values of each point in groups of 4
206 // so that we may operate on all four corners at once
207 vec4 p7 = lum_to(P7, P11, P17, P13);
208 vec4 p8 = lum_to(P8, P6, P16, P18);
209 vec4 p11 = p7.yzwx; // P11, P17, P13, P7
210 vec4 p12 = lum_to(P12, P12, P12, P12);
211 vec4 p13 = p7.wxyz; // P13, P7, P11, P17
212 vec4 p14 = lum_to(P14, P2, P10, P22);
213 vec4 p16 = p8.zwxy; // P16, P18, P8, P6
214 vec4 p17 = p7.zwxy; // P17, P13, P7, P11
215 vec4 p18 = p8.wxyz; // P18, P8, P6, P16
216 vec4 p19 = lum_to(P19, P3, P5, P21);
217 vec4 p22 = p14.wxyz; // P22, P14, P2, P10
218 vec4 p23 = lum_to(P23, P9, P1, P15);
220 // Scale current texel coordinate to [0..1]
221 vec2 fp = fract(tc * rubyTextureSize);
223 // Determine amount of "smoothing" or mixing that could be done on texel corners
224 vec4 ma45 = smoothstep(C45 - M45, C45 + M45, Ai * fp.y + B45 * fp.x);
225 vec4 ma30 = smoothstep(C30 - M30, C30 + M30, Ai * fp.y + B30 * fp.x);
226 vec4 ma60 = smoothstep(C60 - M60, C60 + M60, Ai * fp.y + B60 * fp.x);
227 vec4 marn = smoothstep(C45 - M45 + Mshift, C45 + M45 + Mshift, Ai * fp.y + B45 * fp.x);
229 // Perform edge weight calculations
230 vec4 e45 = lum_wd(p12, p8, p16, p18, p22, p14, p17, p13);
231 vec4 econt = lum_wd(p17, p11, p23, p13, p7, p19, p12, p18);
232 vec4 e30 = lum_df(p13, p16);
233 vec4 e60 = lum_df(p8, p17);
235 // Calculate rule results for interpolation
236 bvec4 r45_1 = _and_(notEqual(p12, p13), notEqual(p12, p17));
237 bvec4 r45_2 = _and_(not(lum_eq(p13, p7)), not(lum_eq(p13, p8)));
238 bvec4 r45_3 = _and_(not(lum_eq(p17, p11)), not(lum_eq(p17, p16)));
239 bvec4 r45_4_1 = _and_(not(lum_eq(p13, p14)), not(lum_eq(p13, p19)));
240 bvec4 r45_4_2 = _and_(not(lum_eq(p17, p22)), not(lum_eq(p17, p23)));
241 bvec4 r45_4 = _and_(lum_eq(p12, p18), _or_(r45_4_1, r45_4_2));
242 bvec4 r45_5 = _or_(lum_eq(p12, p16), lum_eq(p12, p8));
243 bvec4 r45 = _and_(r45_1, _or_(_or_(_or_(r45_2, r45_3), r45_4), r45_5));
244 bvec4 r30 = _and_(notEqual(p12, p16), notEqual(p11, p16));
245 bvec4 r60 = _and_(notEqual(p12, p8), notEqual(p7, p8));
247 // Combine rules with edge weights
248 bvec4 edr45 = _and_(lessThan(e45, econt), r45);
249 bvec4 edrrn = lessThanEqual(e45, econt);
250 bvec4 edr30 = _and_(lessThanEqual(coef * e30, e60), r30);
251 bvec4 edr60 = _and_(lessThanEqual(coef * e60, e30), r60);
253 // Finalize interpolation rules and cast to float (0.0 for false, 1.0 for true)
254 vec4 final45 = vec4(_and_(_and_(not(edr30), not(edr60)), edr45));
255 vec4 final30 = vec4(_and_(_and_(edr45, not(edr60)), edr30));
256 vec4 final60 = vec4(_and_(_and_(edr45, not(edr30)), edr60));
257 vec4 final36 = vec4(_and_(_and_(edr60, edr30), edr45));
258 vec4 finalrn = vec4(_and_(not(edr45), edrrn));
260 // Determine the color to mix with for each corner
261 vec4 px = step(lum_df(p12, p17), lum_df(p12, p13));
263 // Determine the mix amounts by combining the final rule result and corresponding
264 // mix amount for the rule in each corner
265 vec4 mac = final36 * max(ma30, ma60) + final30 * ma30 + final60 * ma60 + final45 * ma45 + finalrn * marn;
268 Calculate the resulting color by traversing clockwise and counter-clockwise around
269 the corners of the texel
271 Finally choose the result that has the largest difference from the texel's original
275 res1 = mix(res1, mix(P13, P17, px.x), mac.x);
276 res1 = mix(res1, mix(P7, P13, px.y), mac.y);
277 res1 = mix(res1, mix(P11, P7, px.z), mac.z);
278 res1 = mix(res1, mix(P17, P11, px.w), mac.w);
281 res2 = mix(res2, mix(P17, P11, px.w), mac.w);
282 res2 = mix(res2, mix(P11, P7, px.z), mac.z);
283 res2 = mix(res2, mix(P7, P13, px.y), mac.y);
284 res2 = mix(res2, mix(P13, P17, px.x), mac.x);
286 gl_FragColor = vec4(mix(res1, res2, step(c_df(P12, res1), c_df(P12, res2))), 1.0);