I ran into a similar issue. I wanted to create an outline around pixels while keeping it pixelated but it was blurring the existing pixels making it look bad. I ended up implementing nearest neighbor then checking the neighbor pixels after calculating the nearest neighbor location to see if the pixel had an alpha greater than 0. If it did I'd fill in the pixel. Here is how I did it:
Outline.fsh:
vec2 nearestNeighbor(vec2 loc, vec2 size) {
vec2 onePixel = vec2(1.0, 1.0) / size;
vec2 coordinate = floor(loc * size) / size;
return coordinate + onePixel / 2.0;
}
void main() {
vec2 onePixel = vec2(1.0, 1.0) / a_sprite_size;
vec4 texture = texture2D(u_texture, nearestNeighbor(v_tex_coord, a_sprite_size)); // Nearest neighbor for the current pixel
if (texture.a == 0.0) {
// Pixel has no alpha, check if any neighboring pixels have a non 0 alpha
vec4 outlineColor = vec4(0.9, 0.9, 0.0, 1.0);
if (texture2D(u_texture, nearestNeighbor(v_tex_coord + vec2(onePixel.x, 0), a_sprite_size)).a > 0.0) {
// Right neighbor has an alpha > 0
gl_FragColor = outlineColor;
} else if (texture2D(u_texture, nearestNeighbor(v_tex_coord + vec2(-onePixel.x, 0), a_sprite_size)).a > 0.0) {
// Left neighbor has an alpha > 0
gl_FragColor = outlineColor;
} else if (texture2D(u_texture, nearestNeighbor(v_tex_coord + vec2(0, onePixel.y), a_sprite_size)).a > 0.0) {
// Top neighbor has an alpha > 0
gl_FragColor = outlineColor;
} else if (texture2D(u_texture, nearestNeighbor(v_tex_coord + vec2(0, -onePixel.y), a_sprite_size)).a > 0.0) {
// Bottom neighbor has an alpha > 0
gl_FragColor = outlineColor;
} else {
// No neighbors with an alpha > 0, don't change the color
gl_FragColor = texture;
}
} else {
// Pixel has an alpha > 0
gl_FragColor = texture;
}
}
You then need to add the shader to your sprite and set the defined attributes on your sprite and shader so values defined in the shader can be used.
spriteNode.setValue(SKAttributeValue(vectorFloat2: vector_float2(Float(spriteNode.size.width), Float(spriteNode.size.height))), forAttribute: "a_sprite_size")
let shader = SKShader(fileNamed: "Outline.fsh")
shader.attributes = [
SKAttribute(name: "a_sprite_size", type: .vectorFloat2)
]
spriteNode.shader = shader
Hopefully this helps anyone else that has a similar issue!