I figured out what was going on. The problem was not floating point precision, and not that I needed to make my own mipmaps to blend each level correctly. There were actually 2 problems:
- I was using the Max Rects packer algorithm with non-power-of-2 sized images to create the texture atlas.
- I wasn't setting
What I was doing:
- Use Max Rects on non-power-of-two packing to create a texture atlas. Textures are aligned at pixel boundaries.
glGenerateMipmap(GL_TEXTURE_2D); making blending the texture atlas textures into each other even on level 1
What I'm doing now:
- All textures are power of 2. 128x128 is the smallest sized texture in a 4096x4096 texture atlas.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 7); - This is new. Don't allow anything below mipmap level 7, because 4096 / (2^7=128) = 32. This makes the code stop generating mipmaps once the smallest texture in the atlas becomes 1 pixel by 1 pixel.
glGenerateMipmap(GL_TEXTURE_2D); - Now scales down the atlas correctly because all the textures are aligned to 1/32 of the width or height of the atlas rather than 1/width or 1/height.
And now the min filter can use 2 modes without texture bleeding:
The following 2 min filter modes still have bleeding issues, but I'm not planning on using them anyway:
The reason is because they are interpolating the mipmaps to sizes that are not powers of two, which makes the textures in the texture atlas aligned to points in between pixels, causing the bleeding. The only way I think this can be solved is by adding a border around each texture in the texture atlas. The only problem with that is that the border would have to be 128 or 64 pixels, the size of the smallest texture or half the size of the smallest texture (if you use half pixel offset). And that's a huge waste of video RAM.
So I went with