A friend recently turned me onto a really cool paper (thanks James!) that Valve wrote that allows you to encode monochromatic (black & white) textures in a way that they can be incredibly low resolution, but when you scale them up, they still look crisp and smooth, not blurry or pixelated.
It is really quite amazing and is perfect for things like fonts or decals.
I recommend reading the paper, but below are some details to help you implement this in your own application, and also some examples of things taken to the extreme.
The paper is here: Improved Alpha-Tested Magniﬁcation for Vector Textures and Special Effects
Here’s a really easy to use program that can turn fonts or SVG files into distance field images: signed-distance-field-font-generator
Ok so, in a signed distance field texture, the alpha value of each pixel is a value of how far that pixel is from the edge of the shape. In a signed distance field, you essentially take the value which is from 0 to 1, and you subtract 0.5 and multiply by 2 so that you change it from 0-1 to -1 to +1. Negative distances mean the pixel is inside the shape, Positive distances mean the pixel is outside the shape.
You only need to do that math if you care about the exact distance though. If you only care about whether the pixel is inside or outside the shape, you can just consider values less than 0.5 to be inside the shape, and values greater than 0.5 to be outside the shape. In other words, you could just do an ALPHA TEST against 0.5 to render these guys.
Here’s an excerpt of some OpenCL code that does this:
float alpha = read_imagef(tex3dIn, g_textureSampler, textureCoords).w; float3 color = (alpha < 0.5f) ? (float3)(1.0f) : (float3)(0.0f);
I'll refer to that code as the "Alpha Test" code.
Another way to do it would be to use smoothstep to smooth the jaggies out a bit. Here's an excerpt of some OpenCL code that does that:
const float smoothing = 1.0/64.0;
float distance = read_imagef(tex3dIn, g_textureSampler, textureCoords).w;
float alpha = Saturate(smoothstep(0.5 – smoothing, 0.5 + smoothing, distance));
float3 color = (float3)(1.0f – alpha);
In the above, the smoothing constant can be adjusted to change how it smooths out the jaggies.
Note that even though the texture is monochromatic, you could use the color channel in the texture if you wanted to, or multiply the color by some other color to make it a colored image.
Here are the two source images I used. The first one is of the "Comic Sans" font which I doubled vertically since my textures have to be square, and the second one is a mustache SVG vector graphics image I found online. The font image is 512×512 and the mustache is 128×128.
Distance Field Textures in Action
Font in Action
Here’s the text taken from 512×512 down to 256×256, rendered with the alpha test code. You can already see degradation unfortunately but the look at the pictures above and remember that the full font texture is essentially 512×256 (I doubled it because my textures have to be square) and looks great up close:
Decal in Action
Apparently another great use for these is to encode a shadow map as a distance field texture. This does a great job of keeping your shadow line smooth, effectively letting you use a much lower resolution texture to store the shadow maps.
The unreal engine allows this as an option in fact, check this link for more info:
Distance Field Shadows
This is a no brainer for static shadows, but dynamic shadows this may not be as useful, as it seems like you’d need to generate the full sized texture to make the distance field texture, so would require some extra memory and processing when generated at runtime. There may be some clever tricks to avoiding that though, not sure.