OpenGL Programming/Modern OpenGL Tutorial Load OBJ

While you can create any shape by typing vertices by hand, it's much more practical to edit shapes from a dedicated editor. Unfortunately, openGL does not include any mesh reading functions. However, the .obj format is quite easy to parse, and most 3d programs, including blender, has export functionality for it. In this tutorial, we will focus on getting Suzan the monkey from blender into our openGL program.

= Creating Suzanne =

Suzanne is the Blender test model. It has 500 polygons, which makes it a good test for us.

To create it, run Blender (we'll use version 2.58), then:
 * Remove all elements from the scene (right click on them and press x)
 * In the top menu, click on Add > Mesh > Monkey
 * Type n to display the Transform panel and
 * set the location to (0, 0, 0)
 * set the rotation to (90, 0, 0)
 * In the top menu, click on File > Export > Wavefront (.obj)
 * to preserve the Blender orientation, carefully set the following options (to switch to "Y-is-up" OpenGL coordinates):
 * Forward: -Z Forward
 * Up: Y Up
 * Tick "Triangulate" so we get triangle faces instead of quad faces

Blender will create two files, suzanne.obj and suzanne.mtl: For now we'll just load the mesh.
 * the .obj file contains the mesh : vertices and faces
 * the .mtl file contains information on materials (Material Template Library)

= File format =

Inspect the .obj file with a text editor. We see that the format is pretty simple:
 * structured in lines
 * lines starting with # are comments
 * o introduces a new object
 * v introduces a vertex
 * vn introduces a normal
 * f introduces a face, using vertex indices, starting at 1

We need to populate several C arrays:
 * the vertices
 * the elements
 * the normals (used for lighting computations)

The format also has other features, but for now we'll leave them aside.

Here's a first, crude implementation that will work for our object.

Our parser is going to be limited (no support for multiple objects, alternative vertex formats, polygons, etc.), but that's enough for our immediate needs. We used C++ vectors to simplify the memory management. We passed arguments by reference, mostly because the syntax to access pointer to vectors becomes horrid

We can load the .obj file this way:

And pass it to OpenGL using:

Last, we adjust our view accordingly, with a Y-is-top coordinates system, and the camera facing Suzanne:

I cheated a bit and implemented a Gouraud lighting model. We'll cover this in a bit.

= Flat-shading - duplicating vertices and normals =

As we discussed in the texturing tutorial, sometimes the same vertex will get different values depending on which face is used. This is the case if we don't want to share normals (and choose an arbitrary face when computing a vertex' normal, as we did above). In that case we need to duplicate the vertex each time it is used with a different normal and then recreate the element array. This will take more time for the loading but it will be faster for OpenGL to process in the long run. The less vertices sent to OpenGL the better. Or, as stated earlier, in this example we will just duplicate the vertices the time they appear; next we can proceed without the element array.

With this setup, we can get flat-shading: the varying variable will actually not vary between vertices in the fragment shader, because the normals will be the same for the 3 vertices of every triangle.

Averaging normals
Our algorithm works, but if two faces reference the same vector, then the last face will overwrite the normal for that vertex. This means the object can look very different depending on the face order.

To remedy this, we can average the normal between the two faces. To average two vectors, you take half of the first vector plus half of the second vector. Here we use  to store the vector coefficient, so we can average with a new vector any number of time, without storing the full list of vectors:



Pre-computed normals
TODO: improve the parser to support .obj normals

The Obj format supports pre-computed normals. Interestingly they are specified in the faces, so if a vertex is present on several faces, it may get different normals, which means we have to use the vertex duplication technique discussed above.

For instance, a basic export of Suzanne references vertex #1 with two different normals #1 and #7: v 0.437500 0.164063 0.765625 ... vn 0.664993 -0.200752 0.719363 ... f 47//1 1//1 3//1 ... f 1//7 11//7 9//7 f 1//7 9//7 3//7

By comparison, the MD2/MD3 format (used by Quake among others) also include pre-computed normals, but they are attached to vertices, not to faces.

= See also =


 * TooL: The OpenGL OBJ Loader, released under the GNU GPL (but OpenGL 1.x)