"Why don’t spritesheets (with an edge around each sprite) and mipmaps go well together and are there any workarounds other than not using spritesheets or not using mipmaps?"
Let’s work through an example, which I shall simplify down to one dimension for clarity. Let’s say we have two textures, sized 4×1:
A B C D
With gutters, we could pack these into a 10×1 sprite sheet:
a A B C D d e E F f
Now let’s compute the first mip level:
A (B+C)/2 D E F
Hmm. That does somewhat resemble the original sheet, but it no longer has proper gutters, so filtering will bleed from one sprite to the other.
Real trouble starts with the second mip level:
(A*2 + B + C + D) / 5 (D + E*2 + F*2) / 5
Our second sprite has disappeared entirely! That rightmost pixel includes colors E and F from sprite #2, but also some amount of color D from sprite #1.
And of course the final mip is entirely meaningless:
(A*2 + B + C + D*2 + E*2 + F*2) / 10
There are two ways you can try to make sprite sheets and mipmaps play together:
- Wishful thinking: include gutters wider than a single pixel, cross your fingers, and hope it turns out ok. Any time you see color bleeding problems, increase the gutter size until they go away.
- The scientific approach: restrict all sprites in a sheet to be the same size, so you can terminate your mip chain at the point where an individual sprite has reached 1×1. Calculate the gutter size so you will still have an entire pixel gutter at this smallest mip level, with corresponding larger gutters for larger mips. Generate mip images separately for each sprite, rather than for the sheet as a whole, and regenerate the gutter with different colors for each mip. This is a considerable pain, and wastes a lot of memory (you end up with significantly more gutter pixels than actual data!) but it does work.
Personally, I would avoid using mipmaps and sprite sheets at the same time. Sprite sheets are never necessary: they’re just an optimization to reduce texture switching. When an optimization ends up causing this amount of trouble, you have to wonder if it’s really worth it?