OpenGL filters and Sprite bleeding

Texture filters in OpenGL bring a lot of troubles.

Here I will describe some of them. You are certainly going to meet these during your game development.

1. The NEAREST filter makes your graphics to look ugly as hell:

1

Well, actually when you keep your sprites unrotated and at the 100% scale it looks just perfect and the problem does not exist. But when you want to scale and rotate… Here is my example: the original scene has the size 800×480 pixels, so on the devices with 800×480 resolution it looks just perfect, except for the arrows broken by rotation (click on image):

ok

The problems come when I run the game on devices with different screen resolutions. I’m using the “Fit-in” camera, which is fitting the image into the device screen while keeping the original aspect ratio. So, independently from the screen resolution, the scene camera nicely scales my game like this:

vert

vert

and here the NEAREST filter makes it ugly (click on image to see “stairs”):

http://jfix.by/wp-content/uploads/2013/01/ug1.png

Summary: the NEAREST filter is not good when you need:
– to scale the scene into the screen properly,
– to rotate sprites (arrows and game objects like airplanes and bombs),
– to do some zoom-ins/zoom-outs with your scene camera.

2. The LINEAR filter.
This one looks relatively fine, but it gives artifacts on sprites:

http://jfix.by/wp-content/uploads/2013/01/artifacts.png

The problem arises from the very nature of using linear sampling. This is called “sprite bleeding” – when texture borders are damaged with jpeg noise from the neighborhood pixels. This well-known problem can be solved by using the Texture Packer. The packer will pad your textures with additional pixels, carefully pack images next to each other reducing bleeding between them, and use a power-of-two size for your texture region to minimize floating point inaccuracies.

Problem solved? No. Bad news. These all work only when you use textures with no transparent pixels. The artifacts still appear on the borders between alpha and non-alpha pixels inside your texture:

problems

So this is what is going to happen with our airplanes:

What’s just happened? Let’s see. Our original PNG-texture consists of 2 parts (or “layers”):
– image data
– and alpha-mask

Together they form our texture:

png

When we scale the image using the LINEAR filter the “layers” get some floating-operations damage, which produces the border:

scaled

Summary: The LINEAR filter is better than NEAREST, but it has this well-known “border”-problem for scaling alpha pixels.

Solution? There is one. Awesome people from the http://blog.gemserk.com/ team were so tired of these filter problems, so they’ve made a tool. The tool properly bleeds the colors of the non-transparent pixels into the transparent ones, so when scaled borders look fine. Here how it works:

The method still doesn’t work for sprites with half-transparent pixels, but… who cares, this is not a common thing. Just ignore it. Source code of the tool is available here: https://github.com/gemserk/imageprocessing.

Now good news everyone! I’ve merged the gemserk’s source code with the available source code of the libGDX TexturePacker and here it is: TexturePackerGemserk.zip

Enjoy!

5 thoughts on “OpenGL filters and Sprite bleeding

  1. That tool looks intense, thanks for sharing. I just wanted to say that even with 1:1 perfect 2D-style sprite textures using GL_NEAREST… you can still have problems. If you start drawing things at non-integral (subpixel) coordinates any textures with a lot of whitespace in them will start throwing up.

  2. Hello there. Dear author.

    I don’t understand 1 thing. If you use the solution from gemserk guys, how will the game draw my tiles/sprites then? Like… how will the game know that it is transparent around the plane? Won’t it just draw the plane with the crazingess going around it? How do you load these images in libgdx after they have been treated with Gemserk solution?

    • Hi Paolo,

      Well, first of all these image operations have really nothing to do with the game engine you use. This is about PNG-files. Basically in this example we have a PNG-file, and this file contains: (1)airplane, (2)simple black “crazingess” around it and (3)alpha mask. Then the Gemserk’s algorithm modifies the PNG and output contains: (1)the same airplane, (3)the same alpha mask and (2)new colorful “crazingess”. What we actually changed here is the content of the “crazingess”. The algorithm didn’t touch the alpha mask, so the information about transparency is exactly the same as it was before. And the PNG-file takes care about the transparency concept itself.

      Now when we have our improved texture (the output PNG-file) we can use it in any game engine we want, the same way we normally use textures:
      https://code.google.com/p/libgdx-sers/wiki/Sprites#Minimal_example
      https://code.google.com/p/libgdx/wiki/TexturesTextureRegionSpriteBatch

      Also there is another thing called AssetsManager:
      https://github.com/libgdx/libgdx/wiki/Managing-your-assets
      that normally helps us to load textures into a game automatically. This AssetsManager can read textures when they are stored in a special format called “texture atlas”. To produce this texture atlas we can use another tool: TexturePacker. This tool processes PNG-files and produces texture atlases for the most of game engines.

      Now, what I did in this article: I just included the Gemserk’s algorithm into one popular version of the TexturePacker, so currently the packer is not only packing textures, but it is also processing them using the Gemserk’s algorithm.

      This is it.

Comments are closed.