Game Development Community

Bilinear Filtering Problems

by Jason Gossiaux · in Torque 2D Professional · 03/17/2016 (12:16 am) · 13 replies

Howdy folks. I've chatted with some of you on IRC about this, but could never really resolve my problem. When using Bilinear filtering, the filtering is applied across the entire packed sprite sheet. So adjacent tiles blend into each other and create visible lines.

It was suggested I make each tile its own sprite to prevent this, but that seems wasteful and may affect the performance somewhat. For creature sprite sheets where the sprites are large and they get close to each other, this is also a problem. Those sheets are animated and so breaking them up isn't possible.

How do developers usually approach this problem?

Thanks!

- Jason

#1
03/17/2016 (3:10 am)
Never managed to fix this issue, except by giving all tiles a black border, which severely hinders your options.

One way I have yet to try is described here and just might work

From GameMaker's docs.
#2
03/17/2016 (3:05 pm)
It is "wasteful" to make each frame a separate image in that you have open file handles and each image carries a little header information (so, disk space). Unless you're targeting a really limited platform, this is still a viable option.

The bilinear filtering problem goes back to how OpenGL does it. As Simon has said, there are some ideas for working around it.
#3
03/17/2016 (10:52 pm)
It is difficult to say how much impact having 1000+ loaded textures versus 25 would be for an iOS application. I suspect you'd have some performance impact. On the IRC folks were concerned it might impact Batching performance if the tilemap was not constructed from frames in a packed textures.

The white outline appears to be a problem with PNGs. I forgot about this as I had encountered it before with TGE. Apparently the alpha channel doesn't save properly with photoshop so you need to use a different program to prevent the filtering from interpreting the alpha as a white background.

I am helping our artist pay attention to scaling distortions when you zoom out to minimize the need for filtering. But in some cases it would be very helpful.

Would trilinear filtering or anisofiltering be a better (or even a practical or possible) option if we researched and implemented it into the engine? Do other filtering methods exist that with some research we may be able to add? I am curious what other 2D game engines support.

Thanks!

#4
03/18/2016 (6:04 am)
If the problem is that you're getting a white background instead of transparency then the issue is definitely that you're not saving transparency to the alpha channel.

Performance impact would vary depending on the underlying hardware. Older iOS devices have trouble running T2D anyway. Testing would tell you if you're within tolerances - nothing beats a real benchmark.
Quote:The bilinear filtering problem goes back to how OpenGL does it.
OpenGL filters the entire image if filtering is applied when what we really want is for each sprite region to be filtered as an independent sample. When we want to use a single image for multiple sprites this is bad, because we get "bleed" from neighboring sprites. The GameMaker doc shows how to "stretch" the edges of your sprite so that the visual artifacts of this are removed.

The resource system could be altered to create individual in-memory images where a sprite asset has multiple frames from a single image and all frames are from the same image. Then each in-memory image could be filtered individually and "bleed" could not occur. I think this would be easier than altering the renderer to correctly handle it.

T2D uses the same (or very nearly the same) rendering code as TGE, since it is a direct descendant of that product. DirectDraw/Direct3D may have been removed - I'd have to go look to be sure.
#5
03/18/2016 (6:14 pm)
Richard is absolutely right (as usual), no easy way around it. All the code does when asked to fetch a frame from an image is to define a rectangular area where the frame should be.

At first glance, setting each frame in a sprite sheet to be individual images in memory would require a pretty deep engine refactoring.
#6
03/18/2016 (9:33 pm)
It would take a fairly deep dive into the resource management and asset systems, that's for sure. It's not a "difficult" thing to do, but I'm thinking it's a broad chunk of code to modify. I believe it would be simpler than trying to get the renderer to deal with it though (might be wrong - maybe Andrew has some ideas).
#7
03/21/2016 (12:26 pm)
Quote:If the problem is that you're getting a white background instead of transparency then the issue is definitely that you're not saving transparency to the alpha channel.

Yeah, we needed to use SuperPNG. Photoshop does transparency just fine, but for filtering it is somehow thinking the transparency is white.

[This is just how opengl works]

I can accept that. A very large number of other games make use of bilinear filtering, however, and viewing their ripped sprite sheets offered no insight into workarounds for the filtering limitations. Wanted to make sure I wasn't missing something.


Quote:Older iOS devices have trouble running T2D anyway.

Well, at least on an iPhone 4s or iPad 3 everything ran acceptably in Torquescript. But since moving everything into C-code I haven't tested. Hope to see some improvements there. We will compare packed vs unpacked and see what differences might exist.


#8
03/21/2016 (6:31 pm)
Quote:I can accept that. A very large number of other games make use of bilinear filtering, however, and viewing their ripped sprite sheets offered no insight into workarounds for the filtering limitations. Wanted to make sure I wasn't missing something.
Newer versions of OpenGL's API certainly handle this better - or the other games that use OpenGL use some other workaround. What I'm trying to say is that this issue has been in T2D/TGE for a long time and it was deemed not worth the time to fix or cover in the last pass before it was MIT licensed - essentially because the version of OpenGL used by T2D applies the filter to the whole sprite sheet instead of to each cell, so that by the time the cells are cropped they're already blurred into one another.
In theory, updating to a newer version of the API might fix it. But I'm betting that even that would require non-trivial changes to the rendering pipeline.
#9
04/07/2016 (9:10 am)
The simple way to solve this is just to put some empty space between the frames in the image. You can then use the CellStride X and Y to skip the empty space in the image. Your frames will stay the same size and the blending will simply flow into the empty space. Problem solved.

Also, I would wager that it's not that hard to solve this at the engine level. Any image handle can have filtering applied to it. It just isn't needed because of the cell stride feature.
#10
04/07/2016 (9:18 am)
Bingo! CellStride is exactly what we needed (and were not aware of) to make this work properly with minimal effort. Thank you!
#11
04/07/2016 (10:33 pm)
Quote:Also, I would wager that it's not that hard to solve this at the engine level. Any image handle can have filtering applied to it. It just isn't needed because of the cell stride feature.
Sure - the problem is that OpenGL runs the filter pass on the whole image, not each individual frame. There was a dev assigned to this specific issue and management decided to just fly with it rather than spend any time on it. I love the CellStride idea - much easier than the other solution.
#12
05/09/2016 (11:08 pm)
I am having an issue with Cellstride. My texture was an 8x8 cell of 64-pixel textures atlased into one 512x512 pixel texture.

After resizing the texture sheet to 576x576, I then spread the textures out so there was a 4-pixel border of transparency around each.

I set the cellwidth to be 64 and the cellstride to be 72. The composite sprite now displays my tiles but with a 4 pixel black border in between each one. I've not been able to remedy this no matter what I have tried.

Any help is appreciated!
#13
05/10/2016 (12:03 am)
Phew. Ok it was the darn offset getting me. I missed reading about how the first cells need to start at location 0,0.

Despite this, I was still getting the white border around my texture cells. Photoshop's stupid bad alpha channel for PNGs. I have a WinXP laptop with a version of SuperPNG that works fine for this, but nothing works on Win10 apparently.

So I followed a guide here: http://answers.unity3d.com/questions/10302/messy-alpha-problem-white-around-edges.html and it solved my problem. Good filter for doing just what you showed earlier :) Yummy filtering.