Texture filtering: mipmaps

In my previous article about texture filtering I mentioned that scaling down images using point sampling, or scaling to less than half size using linear filtering, looks bad because some source pixels are discarded.

Why exactly is this a problem?

Try this thought experiment:

We have a 256x256 texture, which is being scaled down by a factor of 1/256, so the entire image covers just a single screen pixel. What color should this pixel be?

If we use point sampling, only one pixel of the 256x256 image will be used, and the others discarded. This can give pretty random results depending on which specific pixel happens to be chosen!

Consider what happens if we tile our texture, so the scaled down image is repeated 100 times. If each repetition lines up exactly with one screen pixel, they will all choose the same source pixel, but if repetitions and screen pixels do not line up (maybe you are scaling down by 1/255 or 1/257 rather than 1/256, or using a 3D perspective projection), each repetition will end up choosing a different random location in the source texture. This is basically the same thing as:

It would obviously be better if we could average all the pixels from the source texture, rather than having to choose just one of them. But averaging 256x256 color values is too slow to do in realtime.

When you can't afford to compute something on the fly, it is time to precalculate...

A mipmap is a precalculated copy of an image that has been shrunk to a lower resolution using a more accurate, higher quality filtering algorithm than would be possible in realtime on the GPU. Mipmaps are arranged in a chain where each image is half the size of the previous one, for instance:

Original = 256x256

Mip 1 = 128x128

Mip 2 = 64x64

Mip 3 = 32x32

Mip 4 = 16x16

Mip 5 = 8x8

Mip 6 = 4x4

Mip 7 = 2x2

Mip 8 = 1x1

The GPU will automatically choose the appropriate image depending on how much the texture is being shrunk down.

Let's see this in action. Here is the linear filtered terrain from my previous post:

And now using mipmaps:

Note how the distant hills appear smooth and free from noisy aliasing.

Popular urban myth #1

"Mipmaps take up too much memory"

Actually, mipmaps use little extra memory, thanks to the power of powers:

Mip 1 = 1/4 the original size

Mip 2 = 1/16 the original size

Mip 3 = 1/64 the original size

Mip 4 = 1/256 the original size

If you continue this sequence, you'll see that an entire mipmap chain all the way down to 1x1 takes up just 1/3 more memory than the original texture. So it's a negligible overhead, especially once you get past that first mip.

Popular urban myth #2

"Mipmaps are slower for the GPU to render"

In fact, they are often much faster! If you aren't scaling down a texture, having mipmaps won't cost anything. But when you are scaling down, mipmaps can save crazy amounts of memory bandwidth.

Remember our example of a tiled 256x256 texture, where each repeat is being scaled down to 1x1 (a common situation in things like terrain rendering). Without mipmaps, every destination pixel will sample a radically different location in the source texture, so the GPU must jump around fetching colors from different areas of memory. GPUs typically have very small texture caches, relying on the fact that textures tend to be accessed sequentially, so this access pattern will thrash the cache and can bring even a high end card to its knees. But with mipmaps, the GPU can simply load a small mip level which will easily fit in the cache, and can then render many destination pixels without having to go back to main memory.

So mipmaps are not just for reducing aliasing: they can actually speed up rendering, too.

Shawn's Recommendation™: if you are going to draw a texture in 3D, or planning to scale it down to less than half size, you should always use mipmaps.

You can also specify this explicitly using tex2lod in your pixel shader.

tush

24 Nov 2009 3:40 AM

i am rendering the scene onto the texture rather than backbuffer using setRenderTarget in one pass(i am using directx 9.0 shaders). then i am blending that texture with another one and getting the output onto the screen(thats on backbuffer) in second pass. everything is working fine i am also getting the o/p but the problem is the rendered scene which is stored on texture is getting shrunk while rendering on screen. i have also changed the texture size but its not working tried wid projection matrix also.

Ken

9 Jun 2010 4:46 PM

Hi Shawn, does the original texture have to have square dimensions, & does the length have to be a power of 2?

You can scale up using texture filtering (your choice of point, linear, or anisotropic) but mipmaps aren't really applicable here.

What would a precalculated 64x64 mipmap for a 32x32 source image be? That would basically just mean changing your source image to be 64x64 instead, which is entirely possible but not what we would normally call mipmapping!

LEM

22 Jun 2010 1:03 PM

It makes sense. Thanks, Shawn.

Sibop

11 Jun 2011 6:08 PM

I wasn't able to find much info on it, but I was curious about XNA's mipmap generation pipeline. Could you tell me if it does sRGB or gamma aware downsampling, say something around pow(2.2)? I was going to give it a try using a standard black and white striped texture, but figured you might be able to give more detailed info about it. Thanks.

Fermin

21 Aug 2011 2:18 PM

Excellent article.

One question, what if I'm using a texture atlas, say with 4 possible textures in a single one?

I guess the mipmap is just one for the whole texture, so it would cause a lot of glitches, right?