The things nobody bothers to tell you
Posted by: Jason Hughes in Untagged on Aug 19, 2008
Recently, I was putting together a nice tool that takes in any TrueType font and rasterizes a set of glyphs (symbols) into an image at a specified size, to make extremely crisp-looking text for a user interface screen. Won't bore you with the details, but suffice it to say, I liked how it turned out. Unfortunately, mapping a texture exactly to the screen is a little tricky. You see, the texture coordinates and vertex positions together describe an area of the screen to draw to, and in order to make the texture unit draw exactly and precisely the values from the texture, some hardware knowledge is required and some thought must go into "tricking" the texture filter unit to find exactly the color you want in each pixel. Otherwise, you get a blurry mess! No kidding.
So, to make a long story short, I did everything that I was supposed to. Along the way, I found multiple tiny, niggling bugs that would have prevented my text from looking crisp and sharp. But when I exhausted all the possibilities, I tried something that is not mentioned on the sites I looked to for help. I forced my texture to be an exact power of 2 in width and height. And suddenly, it's perfect.
Why? Well, it's pretty simple, really. In order for the texture unit to figure out where to sample from within a texture, it's essentially taking the coordinates U and V and multiplying them by the width and height of the texture, to figure out which texel to fetch. If it's not exactly an integer (1.0 versus 1.0004), you will get a slight amount of filtering. With floating point precision, though, the only way to be really sure this math works out on a pixel-by-pixel basis is to have a power of two texture. The way I understand floating point math, which is quite possibly erroneous, powers of two have zero in the mantissa so are guaranteed to multiply and divide cleanly without introducing errors in the result. In other words, they are invertible: x * 512.0f / 512.0f == x, for all x. At least, within the limits of floating point math.
Ahh.. and I thought allowing the texture take up a bit less space by being odd-sized was a good thing. I hope this eventually helps somebody.
So, to make a long story short, I did everything that I was supposed to. Along the way, I found multiple tiny, niggling bugs that would have prevented my text from looking crisp and sharp. But when I exhausted all the possibilities, I tried something that is not mentioned on the sites I looked to for help. I forced my texture to be an exact power of 2 in width and height. And suddenly, it's perfect.
Why? Well, it's pretty simple, really. In order for the texture unit to figure out where to sample from within a texture, it's essentially taking the coordinates U and V and multiplying them by the width and height of the texture, to figure out which texel to fetch. If it's not exactly an integer (1.0 versus 1.0004), you will get a slight amount of filtering. With floating point precision, though, the only way to be really sure this math works out on a pixel-by-pixel basis is to have a power of two texture. The way I understand floating point math, which is quite possibly erroneous, powers of two have zero in the mantissa so are guaranteed to multiply and divide cleanly without introducing errors in the result. In other words, they are invertible: x * 512.0f / 512.0f == x, for all x. At least, within the limits of floating point math.
Ahh.. and I thought allowing the texture take up a bit less space by being odd-sized was a good thing. I hope this eventually helps somebody.


