#version 450 // EPX (Eric's Pixel Scaler) // based on the description from Wikipedia: // https://en.wikipedia.org/wiki/Pixel-art_scaling_algorithms#EPX/Scale2%C3%97/AdvMAME2%C3%97 // adapted for slang by hunterk // license GPL, I think layout(push_constant) uniform Push { vec4 SourceSize; vec4 OriginalSize; vec4 OutputSize; uint FrameCount; } params; layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } global; #pragma stage vertex layout(location = 0) in vec4 Position; layout(location = 1) in vec2 TexCoord; layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; vTexCoord = TexCoord * 1.00001; } #pragma stage fragment layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Source; bool same(vec3 B, vec3 A0){ return all(equal(B, A0)); } bool notsame(vec3 B, vec3 A0){ return any(notEqual(B, A0)); } // sample with coordinate offsets #define TEX(c,d) texture(Source, vTexCoord.xy + vec2(c,d) * params.SourceSize.zw).rgb void main() { // The algorithm looks at the current pixel and the 4 surrounding cardinal pixels // ___|_A_|___ // _C_|_P_|_B_ // | D | // Our current pixel, P vec3 P = TEX( 0., 0.); // Input pixels vec3 A = TEX( 0., 1.); vec3 B = TEX( 1., 0.); vec3 D = TEX( 0.,-1.); vec3 C = TEX(-1., 0.); // Output: 2x2 grid. Default to the current pixel color (Nearest magnification) // ___one_|_two___ // three | four vec3 one = P; vec3 two = P; vec3 three = P; vec3 four = P; // EPX algorithm rules: // IF C==A AND C!=D AND A!=B => 1=A // IF A==B AND A!=C AND B!=D => 2=B // IF D==C AND D!=B AND C!=A => 3=C // IF B==D AND B!=A AND D!=C => 4=D one = (same(C, D) && notsame(C, A) && notsame(C, B)) ? C : P; two = (same(D, B) && notsame(D, C) && notsame(D, A)) ? D : P; three = (same(A, C) && notsame(A, B) && notsame(A, D)) ? A : P; four = (same(B, A) && notsame(B, D) && notsame(B, C)) ? B : P; vec2 px = fract(vTexCoord * params.SourceSize.xy); // split the texels into 4 and assign one of our output pixels to each FragColor.rgb = (px.x < 0.5) ? (px.y < 0.5 ? one : three) : (px.y < 0.5 ? two : four); FragColor.a = 1.0; }