You can control what happens when texture filtering reads from past the edge of a texture using the SamplerState.AddressU and AddressV properties. But what if you are using a sprite sheet? The sampler address modes know nothing about how your sprites have been packed together, so when you filter a sprite from a sheet, the GPU will happily sample from outside the area you told it to draw. This causes colors from one sprite to show up along the edges of whatever happens to be packed next to it, which is unlikely to be what you wanted!
- Don’t use sprite sheets. This is especially a good idea if you want mipmaps, as it is near impossible to make sprite sheets and mipmaps play nice together.
- Don’t use filtering. Point sampling will never read past the edge of the region you specify, so it has no problem with sprite sheets. But then, point sampling tends to look rather ugly, and who wants that?
- Roll your own filtering in the pixel shader. If you set the GPU to point sampling mode, you could write a shader that implements bilinear filtering by averaging several point samples, and this shader could know about your sprite sheet and thus avoid reading from outside the current sprite. This is likely to be slower than not using sprite sheets at all, though, so not usually a good idea.
- Add a gutter region around each sprite. Given this 3×3 source image:
A B C D E F G H I
Expand it to 5×5:
a a b c c a A B C c d D E F f g G H I i g g h i i
Pack the 5×5 version into your sprite sheet, but just draw the inner 3×3 (shown in bold). Now the filtering hardware can happily read past the edge of the specified region, and will pick up sensible color values from the gutter pixels (shown in italic).
Adding gutters by hand soon gets boring, but this is easy to automate. Our Sprite Sheet sample provides a content processor that will pack any number of textures into a sprite sheet, automatically adding the necessary gutters to make filtering work correctly.