UI Anti-Aliasing

I’ve been working on making a really simple IMGUI implementation for my engine at home. I like to do a little bit of research when I’m approaching something new to me like this, so I went hunting around for publicly available implementations. While doing this, I came across Mikko Mononen’s implementation in Recast.

I was impressed when I ran the demo with how smooth his UI looked. It turns out that he’s using a little trick (which I’d never seen before, but I’m sure is old to many) to smooth of the edges of his UI elements.

Basically, the trick is to create a ring of extra vertices by extruding the edges of the polygon out by a certain amount. These extra vertices take the same color as the originals, but their alpha is set to zero. Mikko calls this ‘feathering’.

In my case, I found that I got good results by feathering just one pixel. Here’s a quick before/after comparison of the my IMGUI check box at 800% zoom:

And here’s a 1-to-1 example showing rounded button corners:

It’s a pretty nice improvement for a very simple technique! If you’re interested in what the code looks like, then either take a look at Mikko’s IMGUI implementation, or you can find the code I use to feather my convex polygons below.

My implementation is a little less efficient since I recalculate each edge normal twice, but I chose to keep it simple for readability.

void CalculateEdgeNormal(float& nx, float& ny, float x0, float y0, float x1, float y1)
{
const float x01 = x1 - x0;
const float y01 = y1 - y0;

const float length = Sqrt(x01 * x01 + y01 * y01);

const float dx = x01 / length;
const float dy = y01 / length;

nx = dy;
ny = -dx;
}

void FeatherConvexPolygon(Primitives& primitives, const Vertex* vertices, int count, float amount, const Texture* texture)
{
Vertex* extruded = Memory::Allocate<Vertex>(Memory::Temp, sizeof(Vertex) * count);

for (int i = 0; i < count; ++i)
{
const Vertex& previous = vertices[(i + count - 1) % count];
const Vertex& current = vertices[i];
const Vertex& next = vertices[(i + 1) % count];

float nx0, ny0, nx1, ny1;

CalculateEdgeNormal(nx0, ny0, previous.x, previous.y, current.x, current.y);
CalculateEdgeNormal(nx1, ny1, current.x, current.y, next.x, next.y);

float nx = (nx0 + nx1) * 0.5f;
float ny = (ny0 + ny1) * 0.5f;

extruded[i] = Vertex(current.x + nx * amount, current.y + ny * amount, Color(current.r, current.g, current.b, 0.0f));
}

for (int i = 0; i < count; ++i)
{
const int j = (i + 1) % count;
AddQuad(primitives, vertices[i], extruded[i], extruded[j], vertices[j], texture);
}

Memory::Free(extruded);
}
view raw FeatherUI.cpp This Gist brought to you by GitHub.

1 Comment »

  1. The technology behind Noesis Gui | EntBlog Said,

    February 7, 2012 @ 7:37 pm

    [...] path to get an approximation of pixel coverage on each pixel. This technique is mentioned in the CodeItNow blog. The problem with this technique is that shapes are slightly [...]

RSS feed for comments on this post · TrackBack URI

Leave a Comment