PrEV
Thoughts from a NeXTStep Guy on Cocoa Development

WaveFront OBJ files and learning Open GL

Dec 29, 2008 by Bill Dudney

Many years ago (way back in college) I wrote a 3D stress visualization application for one of my professors. It was my very first Cocoa application. Written on a NeXT Cube in Display Post Script. Wow, was it cool to work on that app, I had so much fun and learned a ton and got to help with some research. I had so much fun that I got a 2nd credit card, and after maxing both out bought a NeXT slab so I could program at home.

The system that I wrote ended up being a lot like a primitive OpenGL. Ever since then I've been fascinated by 3D graphics. During my life as a Java/IT consultant I never had time to pursue it. Now that I'm an iPhone developer and not constrained by stuff that an IT department want's to pay for I've decided to spend some time investing in getting my brain wrapped around OpenGL.

I have several things I'm thinking about doing, but mostly right now I'm just trying to get a handle on how to make good OpenGL code. Which includes 'old style' fixed function pipeline coding for OpenGL ES as well as programable pipeline (i.e. GLSL shaders) of OpenGL 2.0. While the iPhone currently only supports a fixed function pipeline, the industry is headed towards a programmable pipeline and its only a matter of time (speculation, I have no inside info) before the iPhone has OpenGL ES 2.0 support.

In the future I'd like to do OpenGL/OpenCL integration work too but that will have to wait till Snow Leopard ships. In the mean time I'm going to be reading the spec and playing with it but won't be able to post cause the Snow Leopard impl is under NDA. I will however be posting as I can about what I'm getting out of OpenGL and the implementation on the iPhone (OpenGL ES).

My first task as I saw it was to implement a loader to read in a file format and convert that into an OpenGL image (so I'd have something fun to look at). My kids are into Blender which exports files in the WaveFront OBJ file format. So I figured I'd give it a go and see what I could make happen. I posted on twitter about working on an OBJ file loader and got a response from Brad Larson pointing me to Jeff Lamarche's excellent blog posts on his OBJ file loader. All that being said if you need an OBJ file loader, you might have to write your own. I am mostly just trying to learn OpenGL and not really trying to make a good OBJ file loader (Jeff's goals are similar). I am probably going to do my real work with the Collada file format in the end so I won't be maintaining this loader. Ok back to the point of this post.

Since I'm trying to learn 'the right way' to do OpenGL I spent a bit of time doing research on the various approaches and looked at lots of tutorials and such. Unfortunately the state of OpenGL seems to be one of constant evolution with little disambiguation as to OpenGL 1.0 approaches vs OpenGL 2.0 vs OpenGL 3.0. While I like the fact that OpenGL is constantly evolving and getting better, I don't like spending hrs looking a tutorial only to read in the next tutorial that what I've just learned has been out of date since version 1.5.

After all my reading what I decided is that I would ignore anything that used the 'immediate mode' of vertex uploading as being outdated. Basically 'immediate mode' is any call to glVertex and its kin. These function calls are one of the slowest ways to upload your geometry data to the graphics card. I've heard it explained as 'you walk into the kitchen to make dinner, decide on pasta, go to the store and buy noodles, drive home put them on the counter, go back to the store to buy ground beef, go back home place it on the counter etc.' While this gets the job done you spend an awful lot of time in the car. Sending each vertex out to the card this way is similar, your data spends most of its time on the bus from main memory to the GPU's memory. Non-optimal.

From what I've read the fastest way to get data to the card is via 'Vertex Buffer Objects' or VBO's. A VBO is essentially an array of vertexes (2, 3 or 4 dimensional) that get sent to the card all at once. It is typical to have all the vertexes for an item in a scene be in one VBO (or even for the whole scene). You send the buffer to the card via glBufferData. Since the vertexes are in one long array and are sent in one function call considerable time is saved cause you don't have to initialize the connection to the GPU memory over and over again, the connection is opened and all the data is pushed at once.

Now comes the interesting part, OBJ files are optimized for doing immediate mode processing (I don't know how old the format is, but its ancient in Internet time). So I had to do a bunch of stuff to get my vertexes, normals, colors, materials, and textures to be properly aligned. Jeff seemed to be always a day or two ahead of me and has done a great job of writing up most of what was driving me nuts. So hats off again to Jeff for some great informative posts.

My OBJ file loader is working, I've got it pulling in multiple object OBJ files (i.e. a scene) as well as single object files. It knows how to copy the vertex/normal/texture coords so that everything works like it should. I can get some decent 3D models to show up now. However it is still a mess, each object ends up with its own VBO for each of the vertex attributes (normals and textures). While this works it's much better to interleave the VBO's so that all the data needed to render the object is in one place (many thanks to Eric Wing for this pointer). So I still have a ways to go before it is really good.

I have to finish the iPhone SDK book before I can finish this loader. So I'm going to wait to push the code until after I'm done the book. If you are dying to get your eyes on it feel free to send me an email and I'll send you the ugly undoc'd code.



Comments:

Bill,

glVertex and friends are not supported in OpenGL ES. A very smart decision indeed.

While VBO's are indeed a good thing, I'm not sure it's worth the trouble on the iPhone. They are more efficient in general because they explicitly tell the GPU that the vertex buffers can be stored in dedicated GPU frame buffers. But since the iPhone has a unified memory architecture, it's unlikely to make a major difference: at the end of the day, the hardware needs to fetch the data from the same memory. So if there's any difference, it would have to be because the driver treats them differently. That said, I haven't benchmarked VBO and glVertexBuffer implementations. It would be interested to see some numbers.

Tom

Posted by Tom on December 30, 2008 at 04:43 AM MST #

You've picked up one of the main things for modern OpenGL (glVertex = Bad, Just say to yourself "If I type 'glBegin'... It's broken"), but not the only one.

One of the best things to do is pick up the OpenGL ES 2.0 spec, and use that as your guide. It's not 100% the same as OpenGL 3.0 "pure", but it's 95% there.

Other major points to note are no fixed function pipeline (including matrix stack), and the number of pre-named vertex attributes, varyings and uniforms in shaders has been slashed (e.g. no color, normal, etc per vertex or lighting uniforms). You now build your own custom ones. It's makes the whole thing a much more general solution.

Posted by Paul Sargent on December 30, 2008 at 04:43 AM MST #

You're good at writing on some of the more esoteric areas of Mac development. If you have a chance to do so, consider a blog post "introducing" OpenGL development in general based on what you've been doing - because you're right, although there's good docs from Apple with respect to OpenGL on MacOSX, sometimes it's hard to sift through the glut (heh) of years of accumulated OpenGL articles/tutorials and see a straightforward "path" to learning.

Posted by Erik on December 30, 2008 at 08:21 AM MST #

Bill, we have an OpenGL-intensive app (Grafly) and, as Tom suggests, going to VBO made no real performance difference. So VBO seems to be a wash on the current iPhone.

Posted by Chris Ryland on December 30, 2008 at 09:46 AM MST #

Tom, I have run benchmarks on VBOs on the iPhone, and they do indeed make a big difference. I saw a 4-5X rendering performance improvement by using VBOs with static objects that just needed to be rotated and zoomed. Even though the iPhone uses a unified memory architecture, you can run Instruments when using vertex arrays and watch as geometry copying operations become the bottleneck for your application. The geometry is still being copied from one memory location to another for use with the GPU on every frame. VBOs remove that step.

Bill, one thing you'll need to watch for in constructing VBOs is the fact that the largest index data type you can use is unsigned shorts. Therefore, if you have an object with more than 65,536 vertices, you will need to use multiple VBOs to represent that object. This caused me some trouble, as the iPhone Simulator supports larger data types, but the actual hardware does not.

Posted by Brad Larson on December 30, 2008 at 12:12 PM MST #

Hi Brad,

Thanks for the pointer on index data size I will have to update my loader and make sure I'm not pushing more than 2^16 vertices at a time.

Posted by Bill Dudney on December 30, 2008 at 12:12 PM MST #

Hehe, feel free to add as much as you'd like on this topic in the book. Got an OpenGL ES book from New Zealand (the taxes were 1.5x the price of the book!! Crazy!!) and I'm looking forward to dive into the topic in a couple of months time. :-) Or perhaps an iPhone ES book? :-) Should you find some ObjC classes that help the implementation so that we can do this the ObjC way rather than the C-only way, do send some pointers. :-)

Anyway, I'm looking forward to reading the rest of your book. Keep up the good writing :-)

Posted by Nik on December 30, 2008 at 02:46 PM MST #

Hey Bill... OpenGL loaders were something a buddy and I researched for quite a while. There are actually a few ok "open source" loaders written in C++ that you can use to leverage. I didn't find one for the WaveFront format, but I found them for some of the other popular formats. In any case, if you create a solid loader, I hope you open source it as there is a huge lack of decent loaders.

Posted by Jeff Genender on December 31, 2008 at 09:13 AM MST #

Post a Comment:
  • HTML Syntax: Allowed