Jump to content

How to Render Anti-Aliased Lines with Textures in iOS 4

+ 1
  adfm's Photo
Posted Jun 24 2010 10:49 AM

Want to cut some of that overhead incurred while rendering a scene? Why use full-screen anti-aliasing when you don't have to? This shortcut from Philip Rideout's iPhone 3D Programming will show you how to render anti-alias lines with textures.


Sometimes full-screen anti-aliasing is more than you really need and can cause too much of a performance hit. You may find that you need anti-aliasing only on your line primitives rather than the entire scene. Normally this would be achieved in OpenGL ES like so:

glEnable(GL_LINE_SMOOTH);

Alas, none of the iPhone models supports this at the time of this writing. However, the simulator does support line smoothing; watch out for inconsistencies like this!

A clever trick to work around this limitation is filling an alpha texture with a circle and then tessellating the lines into short triangle strips (Figure 6.9). Texture coordinates are chosen such that the circle is stretched in the right places. That has the added benefit of allowing round end-cap styles and wide lines.

Figure 6.9. Line anti-aliasing with textured triangle strips

Attached Image

Using a 16×16 circle for the texture works well for thick lines (see the left circle in Figure 6.9 and left panel in Figure 6.10). For thinner lines, I find that a highly blurred 16x16 texture produces good results (see the right circle in Figure 6.9 and right panel in Figure 6.10).

Figure 6.10. Antialiased lines

Attached Image

Let’s walk through the process of converting a line list into a textured triangle list. Each source vertex needs to be extruded into four new vertices. It helps to give each extrusion vector a name using cardinal directions, as shown in Figure 6.11, “Line extrusion”.

Figure 6.11. Line extrusion

Attached Image

Before going over the extrusion algorithm, let’s set up an example scenario. Say we’re rendering an animated stick figure similar to Figure 6.10. Note that some vertices are shared by multiple lines, so it makes sense to use an index buffer. Suppose the application can render the stick figure using either line primitives or textured triangles. Let’s define a StickFigure structure that stores the vertex and index data for either the non-AA variant or the AA variant; see Example 6.17. The non-AA variant doesn’t need texture coordinates, but we’re including them for simplicity’s sake.

Example 6.17. Structures for the extrusion algorithm

struct Vertex {

	vec3 Position;

	vec2 TexCoord;

};



typedef std::vector VertexList;

typedef std::vector IndexList;

	

struct StickFigure {

	IndexList Indices;

	VertexList Vertices;

};


The function prototype for the extrusion method needs three arguments: the source StickFigure (lines), the destination StickFigure (triangles), and the desired line width. See Example 6.18 and refer back to Figure 6.11 to visualize the six extrusion vectors (N, S, NE, NW, SW, SE).

Example 6.18. Line extrusion algorithm

void ExtrudeLines(const StickFigure& lines, StickFigure& triangles, float width)

{

	IndexList::iterator sourceIndex = lines.Indices.begin();

	VertexList::iterator destVertex = triangles.Vertices.begin();

	while (sourceIndex != lines.Indices.end()) {

 	

 	vec3 a = lines.Vertices[lines.Indices[*sourceIndex++]].Position;

 	vec3 b = lines.Vertices[lines.Indices[*sourceIndex++]].Position;

 	vec3 e = (b - a).Normalized() * width;



 	vec3 N = vec3(-e.y, e.x, 0);

 	vec3 S = -N;

 	vec3 NE = N + e;

 	vec3 NW = N - e;

 	vec3 SW = -NE;

 	vec3 SE = -NW;

 	

 	destVertex++->Position = a + SW;

 	destVertex++->Position = a + NW;

 	destVertex++->Position = a + S;

 	destVertex++->Position = a + N;

 	destVertex++->Position = b + S;

 	destVertex++->Position = b + N;

 	destVertex++->Position = b + SE;

 	destVertex++->Position = b + NE;

	}

}


At this point, we’ve computed the positions of the extruded triangles, but we still haven’t provided texture coordinates for the triangles, nor the contents of the index buffer. Note that the animated figure can change its vertex positions at every frame, but the number of lines stays the same. This means we can generate the index list only once; there’s no need to recompute it at every frame. The same goes for the texture coordinates. Let’s declare a couple functions for these start-of-day tasks:

void GenerateTriangleIndices(size_t lineCount, IndexList& triangles);

void GenerateTriangleTexCoords(size_t lineCount, VertexList& triangles);

Flip back to Figure 6.9, and note the number of triangles and vertices. Every line primitive extrudes into six triangles composed from eight vertices. Since every triangle requires three indices, the number of indices in the new index buffer is lineCount*18. This is different from the number of vertices, which is only lineCount*8.

Example 6.19. Line extrusion initialization methods

void GenerateTriangleIndices(size_t lineCount, IndexList& triangles)

{

	triangles.resize(lineCount * 18);

	IndexList::iterator index = triangles.begin();

	for (GLushort v = 0; index != triangles.end(); v += 8) {

 	*index++ = 0 + v; *index++ = 1 + v; *index++ = 2 + v;

 	*index++ = 2 + v; *index++ = 1 + v; *index++ = 3 + v;

 	*index++ = 2 + v; *index++ = 3 + v; *index++ = 4 + v;

 	*index++ = 4 + v; *index++ = 3 + v; *index++ = 5 + v;

 	*index++ = 4 + v; *index++ = 5 + v; *index++ = 6 + v;

 	*index++ = 6 + v; *index++ = 5 + v; *index++ = 7 + v;

	}

}



void GenerateTriangleTexCoords(size_t lineCount, VertexList& triangles)

{

	triangles.resize(lineCount * 8);

	VertexList::iterator vertex = triangles.begin();

	while (vertex != triangles.end()) {

 	vertex++->TexCoord = vec2(0, 0);

 	vertex++->TexCoord = vec2(0, 1);

 	vertex++->TexCoord = vec2(0.5, 0);

 	vertex++->TexCoord = vec2(0.5, 1);

 	vertex++->TexCoord = vec2(0.5, 0);

 	vertex++->TexCoord = vec2(0.5, 1);

 	vertex++->TexCoord = vec2(1, 0);

 	vertex++->TexCoord = vec2(1, 1);

	}

}


Et voilà…you now know how to render antialiased lines on a device that doesn’t support antialiased lines! To see this in action, check out the AaLines sample from this book’s example code.

iPhone 3D Programming

Learn more about this topic from iPhone 3D Programming.

Do you have a great idea for a graphics-intensive iPhone or iPad application, but don't know how to bring it to life? This book offers the perfect solution: a crash course on the OpenGL graphics library with an overview of iPhone 3D development. Whether you're an experienced OpenGL developer looking to build iPhone apps for the first time, or an iPhone developer wanting to learn sophisticated graphics, iPhone 3D Programming addresses both in one concise, easy-to-use guide.

See what you'll learn


Tags:
1 Subscribe


1 Reply

0
  RoryF's Photo
Posted Mar 15 2012 09:42 AM

Great article! One question, where can I get vec2 and vec3? They don't appear to be included in my project by default.