Blog

Counting FPS

Monday, 02 November 2009.

In the previous article, we saw how to create a sphere, which we’ll now use in this project as basis for a simple benchmark:

SphereBenchmark2

AnXcode project for the iPhone is provided with full src code, which you can use in your own project. The core code is written in plain C, so the project should be very easy to port over to GLUT or any other framework.

There were several driving factors that motivated me to create this project. I wanted to learn:

1 how many primitive (unlit, untextured) triangles can my iPhone render?
2 how to implement FPS (Frames Per Second) counter?
3 how to implement a simple HUD (Heads Up Display)?
4 how to use textures?
5 how to render text using OpenGL only?
6 how to handle changes to screen orientations?

Answer 1: On my 3G iPhone the answer appears to be around 30k triangles per second  (the number of triangles per frame is printed out to stderr, so check your log). The project as it is currently setup, renders 4936 triangles per frame at 60 fps. Also notice that the grid rendered on top of the spheres has a different triangle count that the sphere itself. See pen.c for more details.

Answer 2: To be useful, the FPS counter updates its value on the screen once per second (more often updates make the reading difficult), with a value that is an average of all sampled frames in the past 1 second. In addition to the FPS value, I personally found it useful to display a second line underneath showing the utilization, which quickly tells me if the GPU is maxed out or not, and how much more stuff I could push. See fps.c for more details.

Answer 3: The HUD is a simple transparent rectangle, rendered using glOrthof projection, which uses alpha blending to allow the scene underneath it to show through. See fps.c for more details.

Answer 4: All the GPU cares about are raw pixel values, so we can load a texture in any way we want to, or generate one procedurally. Just before uploading the pixels, however, we need to make sure that they  are right side up. Also, and this is very important or otherwise your texture will not show up, we must define GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Lastly, if the texture uses alpha pixels and is transparent, the blend function to use is glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). See fps.c for more details.

Answer 5: To render text using only OpenGL, we need two things: font texture and the glyph positions. To generate font texture I used PC utility called bmfont which also generates glyph positions in several different formats. I picked xml, but then instead of hooking in xml parser I just manually created a table for the 16 glyphs I use in this project. For real apps we would obviously need to use the actual xml. See EAGLView.m for more details.

Answer 6: The screen orientation is easily handled with glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z) with the only caveat being that for the orthogonal projection we also need to apply translations. See tilt.c for more details.

Creating sphere from a rectangular mesh

Saturday, 31 October 2009.

In the previous article, we saw how to create a rectangular mesh using one long triangle strip. I wrote that such a mesh can be used to wrap around a sphere and now I will show how to do it:

Sphere0

AnXcode project for the iPhone is provided with full src code, which you can use in your own project. The core code is written in plain C, so the project should be very easy to port over to GLUT or any other framework.

Note: for this project we made the choice to have X-axis run from left to right, Z-axis from the back of the screen towards the front, and our Y-axis to run from the bottom of the screen towards the top. Our sphere’s radius will be defined as span.

We start by wrapping the 2D mesh into a 3D tube:

Sphere2

We do this by choosing two opposite edges and stitching them together by using the same vertex values. We generate point values P(x,y, z), where y = [-span, ..., 0, ..., span] by using Eq. 1a as shown in Fig. 1, except that our points are P(x,z), not P(x,y):

Sphere8

At this point if we were to set r=0 for the top and bottom rows, and r=span for the rest of the rows then using Eq. 1.a we’d generate a solid cylinder:

Sphere3

We want to create a sphere, however, so we have a little bit more work to do. Our task is to find radius for each row (for the cylinder above, we in fact got 3 rows right already: the top, bottom and the middle row). We can find the radius values using Eq. 1b from Fig. 1. where our r=span, y is generated by some function with range of [-span, ..., 0, ..., span].

A first effort to generate the y would be to use a simple linear function f(x)=y. If we do that, we will create this interesting sphere:

Sphere9

We generated a sphere with bold spots at top and bottom. It lacks detail at the north and south zenith, and instead allocates more triangles to the rest of the rows, which might be exactly what we want, if we wanted to use it for sky view, for example.

To generate “perfect” sphere, however, all we need is to do is to use the circle equation again. Conceptually, we want to use Eq. 1a again, but this time we are only interested in generating y values for half of the circle, so all we need is just one part of the equation, ie: x= r * sin(angle) where r=span, angle is generated by linear function with domain [-span, ..., 0, ..., span] and range [0, 180]

Once we calculate the x, we use it as our y back in Eq. 1b. This time we get our final sphere:

Sphere10

The code with all the pieces put together:

void meshWrapAroundSphere(Mesh *mesh)
{
  GLuint vertColumns = mesh->nrOfVerticiesInRow;
  GLuint vertRows = mesh->nrOfVerticiesInColumn;

  GLfloat heightStep = (2.0f / (vertRows-1));
  GLfloat height = -1.0f;

  GLfloat angleStep = DEGREES_TO_RADIANS(360.0f / (GLfloat)(vertColumns-1));

  for (int r=0; r<vertRows; r++)
  {
    GLfloat heightScaled = mesh->span * sinf(DEGREES_TO_RADIANS(height*90.0f));
    GLfloat radius = 0.0f;
    if ((r > 0) && (r < (vertRows-1)))
    {
      radius = sqrtf((mesh->span*mesh->span)-(heightScaled*heightScaled));
    }

    GLfloat angle = angleStep;
    for (int c=0; c<(vertColumns-1); c++)
    {
      MeshVertex *vertex = &mesh->verticies[r*vertColumns + c];
      vertex->y = heightScaled;
      vertex->x = (radius * cosf(angle));
      vertex->z = (radius * sinf(angle));

      if (c == 0)
      {
         MeshVertex *vertex2 = &mesh->verticies[r*vertColumns + (vertColumns-1)];
         vertex2->x = vertex->x;
         vertex2->y = vertex->y;
         vertex2->z = vertex->z;
      }
      angle += angleStep;
    }
    height += heightStep;
  }
}

spacer

While researching this topic I found the following page useful: http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/sphere_cylinder/

Programmatically generating a rectangular mesh using single GL_TRIANGLE_STRIP

Saturday, 24 October 2009.

Apple recommends to use OpenGL GL_TRIANGLE_STRIP and VBOs for iPhone geometry, so when I started looking into terrain generator, the very first question I asked myself was how to programmatically generate a rectangular mesh using single GL_TRIANGLE_STRIP.  After a bit of research and coding I wrote this simple mesh generator:

MeshScreenshot

An Xcode project for the iPhone is provided with full src code, which you can use in your own project. The mesh generator provides two APIs (see mesh.h): one which takes number of verticies in column and row, and the other one which takes desired triangle count. The core code is written in plain C, so the project should be very easy to port over to GLUT or any other framework.

A mesh, for our purposes is defined as a rectangular piece of geometry, defined by its vertices. A very simple example of our mesh is a 3×3 rectangle defined by 9 vertices (8 triangles) – see Fig. 1:

MeshFig1

Such mesh is pretty easy to generate (see meshCreate in mesh.c) and is stored in an array of MeshVertex structures, with 3 GLfloat per entry. Those vertices will be later uploaded to GPU using glBufferData (see _meshCreate in pen.c). Next we need to create an array of vertex indicies that will tell the GPU how to construct our triangles – see Fig. 2:

MeshFig2

Looking at Fig. 2a and Fig. 2b it’s easy to see how the indicies for strips are generated. Triangle #1 is defined by indicies 1, 4 and 2, triangle #2 is defined by indicies 4, 2 and 5, and so on… This is what makes the triangle strips so efficient – the 1st triangle requires 3 vertices (9 GLfloats), but to define the next one, all we need is just one additional explicit vertex (3 GLfloats). The indicies are stored in an array of GLushort. The implications of using GLushort, for storing the indicies is that the mesh can not have more than 2^16 (ie. 65536) entries. A good optimization here would be to use the smallest, but large enough type to hold our indicies (ie, GLushort or GLubyte).

Now that we know how to create strips that define mesh rows, we need to figure out how to stitch the strips together to create one long strip – see Fig. 3:

MeshFig3

Our task here is to tell the GPU how to jump from the end of one strip to the beginning of the next one. The solution is to insert degenerate (zero area) triangles – such triangles will not be drawn by the GPU. A degenerate triangle is defined by 3 verticies, at least 2 of which are the same. In our case, we will repeat last index of the previous strip and first index of the next one. By inserting 2 extra indicies we create 4 additional (degenerate) triangles, ie:

- triangle #1 defined by indicies: 3, 6, 6

- triangle #2 defined by indicies: 6, 6, 4

- triangle #3 defined by indicies: 6, 4, 4

- triangle #4 defined by indicies: 4, 4, 7

That’s it. To render the mesh all we need to do is to call glDrawElements once per frame. Such a mesh can be used to render more than just terrain, we could even use it to wrap around a sphere if we wanted to. Too bad OpenGL has no such thing as “GL_TRIANGLE_MESH” - it wouldn’t even need the explicit index array, if it could assume only rectangular meshes.

spacer

While researching this topic I found the following discussions useful: http://www.gamedev.net/community/forums/topic.asp?topic_id=208950 http://www.gamedev.net/reference/articles/article1871.asp

For a quick overview of GL_TRIANGLE_STRIP please see this wikipedia entry; the advantage here is that triangle strips save VRAM and lay out vertices in optimal format for the GPU, both of which contribute to higher performance (this optimization is not as important for desktop GPUs).

For a quick overview of VBOs please see this wikipedia entry; the theory here is that we eliminate copying of vertices from RAM (CPU) to VRAM (GPU) whenever we need to draw them, by uploading the verticies only once, which again helps the performance (today’s iPhone/iPods use integrated memory, which means that this optimization is not currently as important).