O'Reilly logo

iPhone 3D Programming by Philip Rideout

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Assembling Primitives from Vertices

The 3D shape of an object is known as its geometry. In OpenGL, the geometry of an object constitutes a set of primitives that are either triangles, points, or lines. These primitives are defined using an array of vertices, and the vertices are connected according to the primitive’s topology. OpenGL ES supports seven topologies, as depicted in Figure 2-2.

Primitive topologies

Figure 2-2. Primitive topologies

Recall the one line of code in HelloArrow from Chapter 1 that tells OpenGL to render the triangles to the backbuffer:

glDrawArrays(GL_TRIANGLES, 0, vertexCount);

The first argument to this function specifies the primitive topology: GL_TRIANGLES tells OpenGL to interpret the vertex buffer such that the first three vertices compose the first triangle, the second three vertices compose the second triangle, and so on.

In many situations you need to specify a sequence of adjoining triangles, in which case several vertices would be duplicated in the vertex array. That’s when GL_TRIANGLE_STRIP comes in. It allows a much smaller set of vertices to expand to the same number of triangles, as shown in Table 2-1. In the table, v is the number of vertices, and p is the number of primitives. For example, to draw three triangles using GL_TRIANGLES, you’d need nine vertices (3p). To draw them using GL_TRIANGLE_STRIP, you’d need only five (p + 2).

Table 2-1. Primitive counts

TopologyNumber of primitivesNumber of vertices
GL_POINTSvp
GL_LINESv / 22p
GL_LINE_LOOPvp
GL_LINE_STRIPv - 1p + 1
GL_TRIANGLESv / 33p
GL_TRIANGLE_STRIPn - 2p + 2
GL_TRIANGLE_FANn - 1p + 1

Another way of specifying triangles is GL_TRIANGLE_FAN, which is useful for drawing a polygon, a circle, or the top of a 3D dome. The first vertex specifies the apex while the remaining vertices form the rim. For many of these shapes, it’s possible to use GL_TRIANGLE_STRIP, but doing so would result in degenerate triangles (triangles with zero area).

For example, suppose you wanted to draw a square shape using two triangles, as shown in Figure 2-3. (Incidentally, full-blown OpenGL has a GL_QUADS primitive that would come in handy for this, but quads are not supported in OpenGL ES.) The following code snippet draws the same square three times, using a different primitive topology each time:

const int stride = 2 * sizeof(float);

float triangles[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 1}, {1, 0}, {0, 0} };
glVertexPointer(2, GL_FLOAT, stride, triangles);
glDrawArrays(GL_TRIANGLES, 0, sizeof(triangles) / stride);

float triangleStrip[][2] = { {0, 1}, {0, 0}, {1, 1}, {1, 0} };
glVertexPointer(2, GL_FLOAT, stride, triangleStrip);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(triangleStrip) / stride);

float triangleFan[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 0} };
glVertexPointer(2, GL_FLOAT, stride, triangleFan);
glDrawArrays(GL_TRIANGLE_FAN, 0, sizeof(triangleFan) / stride);
Square from two triangles

Figure 2-3. Square from two triangles

Triangles aren’t the only primitive type supported in OpenGL ES. Individual points can be rendered using GL_POINTS. The size of each point can be specified individually, and large points are rendered as squares. Optionally, a small bitmap can be applied to each point; these are called point sprites, and we’ll learn more about them in Chapter 7.

OpenGL supports line primitives using three different topologies: separate lines, strips, and loops. With strips and loops, the endpoint of each line serves as the starting point for the following line. With loops, the starting point of the first line also serves as the endpoint of the last line. Suppose you wanted to draw the border of the square shown in Figure 2-3; here’s how you could do so using the three different line topologies:

const int stride = 2 * sizeof(float);
	
float lines[][2] = { {0, 0}, {0, 1},
                     {0, 1}, {1, 1},
                     {1, 1}, {1, 0},
                     {1, 0}, {0, 0} };
glVertexPointer(2, GL_FLOAT, stride, lines);
glDrawArrays(GL_LINES, 0, sizeof(lines) / stride);

float lineStrip[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0} };
glVertexPointer(2, GL_FLOAT, stride, lineStrip);
glDrawArrays(GL_LINE_STRIP, 0, sizeof(lineStrip) / stride);

float lineLoop[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 0} };
glVertexPointer(2, GL_FLOAT, stride, lineLoop);
glDrawArrays(GL_LINE_LOOP, 0, sizeof(lineLoop) / stride);

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required