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);}



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 [...]