The first thing we need is a way to wrap the text in a diamond shape. I haven’t yet discovered the perfect method for doing this and my implementation turned out somewhat messy and full of checks for different cases so I’ll leave this one bit out as an exercise for the reader. Assuming we did an okay job we can feed our method a piece of text and get a result that looks somewhat like this.

Well.. it is kind of the diamond shape we wanted. Next we will calculate the rectangle of each line of text and then combine all the lines to get the polygon representing the shape of our text. This is the piece of geometry that will be transformed into a speech bubble.

Somehow this polygon has to become a smooth oval and it turns out we need to do two relatively simple transformations to achieve it. First, let’s connect those vertices on the edges so we get a convex polygon.

The resulting polygon (in green) is the convex hull of the set of points making up our text shape. There are many algorithms that can do this for us and for our small set of points it doesn’t matter that much which one we’ll pick. A Google search for “Convex hull algorithm” brings up many implementations and variants, any correct one will work for this.

The computed convex hull gives us a result that isn’t yet visually pleasing, but the shape is getting closer to what we want. The next part is really simple, we’ll find the center of our polygon and displace all the vertices away from it which gives us some nice padding.

This is already much better, certainly not that far away from our desired shape, only needs to be smoothed. We will perform an iterative subdivision process where we add new vertices and displace the old ones towards the mid-points of their new neighbors (it’s much simpler than it sounds). Here is an illustration that better explains the technique:

For each existing edge (blue), we add a new vertex (red) at its midpoint. Then we displace the old vertices (green) towards the median of their respective new neighbors (red).

The image above shows us the result of applying the subdivision process three times consecutively. We start seeing diminishing returns after 3 iterations and that’s how many we decided to use for our game. The desired shape has been achieved, but we are still missing a critical piece.

There. A triangle for the direction arrow and our speech bubble is starting to look nicely. We still have to add an outline and deal with the aliasing so it’s time to turn to the GPU for help.

We’ll use a shader implementing the Sobel operator which computes the outlines of our polygon by examining its pixels and we’ll use that information to shade our outlines in black. Here is an example implementation of the Sobel operator in GLSL.

float sobel(in sampler2D texture, in vec2 step, in vec2 uv) {

float tl = texture(texture, uv + vec2(-step.x, step.y)).b;

float left = texture(texture, uv + vec2(-step.x, 0.0)).b;

float bl = texture(texture, uv + vec2(-step.x, -step.y)).b;

float top = texture(texture, uv + vec2(0.0, step.y)).b;

float bottom = texture(texture, uv + vec2(0.0, -step.y)).b;

float tr = texture(texture, uv + vec2(step.x, step.y)).b;

float right = texture(texture, uv + vec2(step.x, 0)).b;

float br = texture(texture, uv + vec2(step.x, -step.y)).b;



float x = tl + 2.0 * left + bl - tr - 2.0 * right - br;

float y = -tl - 2.0 * top - tr + bl + 2.0 * bottom + br;

return clamp(sqrt(x*x + y*y), 0.0, 1.0);

}

After running this on our mesh we get this result back.

Not bad at all. All that’s left is to do apply some anti-aliasing to smooth those rough edges. I decided to go with FXAA, but any post-processing AA technique should work. In our engine we draw the speech bubbles to an off-screen buffer so we can run FXAA on them separately from the rest of the scene. If you are making a 3D game then you most likely already have an anti-aliasing step and do not need to do this separately.

And there it is, a beautiful speech bubble that can fit pretty much any piece of text we throw at it (with minor tweaking). At this point you can think of ways to add animation or additional effects like drop shadow.