Skip to content

Archive

Category: OpenGL-ES

Hairlock - dev picHere’s a quick glimpse of the forest covering about a quarter of Hairlock’s tiny world. You bet the large flowers that you see at the bottom are generated/cloned on the fly.

How much plant growth can I afford within frame rate? Probably not as much as I’d like, but little is better than nothing and at least the ground recipe is set. Here’s a quick summary:

  • I define growth per material. So if the terrain has grass, something can grow on the grass, and so forth.
  • Growth isn’t just random. It uses random sources, then randomizes plant locations within (it could be anything. Bones? Bones grow in the desert, right?) and their density. This helps a little because plants aren’t just there to decorate. Pseudo-random distributions also help the player orient themselves a little, acting as landmarks in the landscape.
  • It’s not too hard to hit the roof on how many decorations can be added to a 3D scene. If you double the definition of a 3D model, you don’t double the number of pixels rendered for that model. On the other hand, if I double the number of flowers in a scene, you really draw twice as many pixels over.
  • Better news, there’s no practical limit to how many species of plants you might throw in your game. In this scene, I have only a couple of species (water lillies lost in the fog on the left) but that’s not a rendering limitation – from a design point of view, the trick is not to throw everything in at the same time: if we get our players to find stuff less often, we’re surprising them more often.
  • A side effect of having to pack geometry together (well yea… we can’t call glDrawElements once per flower, right?) is that it’s probably OK to vary the size, shape and color of each element. Haven’t tried yet.

Putting this up has been a little troublesome. as I’ve just mentioned, we can’t just draw each element on the fly, which makes things a little harder

  • For a given terrain (a piece of the 3D world), sources for growth are evaluated the first time the terrain is rendered. Each source will contain many instances of the same decoration, but we don’t evaluate anything at this point.
  • Sources pass a test to make sure they’re in-bound. The first time a source passes the test, memory is allocated for all flowers (or whatever) within that source. Then the terrain is intersected to find the insertion point of each element.
  • For each source, geometry for each element has to be duplicated (remember, we’re trying to call glDrawElements less often). This means the allocation for each source is… sizable. Surely we don’t want to evaluate each rendered source every time. So we cache the sources. So we quickly arrive at a point where the memory is filled with, wow, bouquets of low poly models (here, roughly 30 triangles each).
  • In a first approach, I release geometry for sources that haven’t been drawn for a while.

The trees in the pic are also randomized. But these are large enough (and the geometry is much heavier) so I just use gl operations to transform and render each tree.

By the way these trees have been around for at least a month, time to change them for something better I guess :) The decorator module wasn’t really hard to write – just a little more than an evening’s job.

Hairlock - dev picBy the calendar, I fixed many obvious bugs in my game engine up to the 1st of March. Since last week, and for hopefully no more than another dozen days, I’ll only be working on game assets.

Guess it’s time to post a pic then – the project still goes by the codename Hairlock - not as good as it used to be since the hairlock incident proper has been moved to a proposed sequel.

I started modeling a couple of months back, among worries that the rendering wouldn’t follow. Now the question is different: can the artwork follow?

One question that comes to me over and over about the artwork is, why don’t I borrow/buy stock models? I doubt whether this would actually be feasible granted my geometry has to stand for (no) textures, and game models following the twist may be hard to find. But that’s not it. Nor taking the challenge (well yeah, in part…). I just have this feeling that even amateur artwork will be more interesting than even professionally produced stock models – granted it is designed to be coherent and originally produced.

Sure I could use borrow or buy artwork for little things – one chair here, a house there, and so forth but…

The actual cost for me is characters. The little guys on the right took me about 15 hours to get done with. And we don’t want to borrow characters, do we?

Artwork is expensive, but we don’t need to pay every time

The bottom line is…

  1. Since a game is its own universe, it may be a good idea to create original artwork for it.
  2. Within the game, reusing artwork elements increases consistency without compromising quality.

One of the great advantages of 3D is that 3D is vector graphics. While the cost of a new model is high, dressing up a model in different ways, or just changing it a little, is fairly cheap – if you’re using Blender and you’re out making actual geometry for clothes or hair, retopology may be worth a try. That, and a little patience.

So while I enjoy making new stuff, I also look forward to just pulling models off the hat later on, so I can focus more on story and game mechanics.

About the screenshot…

Here we’re running at around 13 FPS. The models use no textures, so everything you see is (un-decimated) geometry.

  • Our little heroine on the left renders 1,588 vertices / 1623 faces
  • The wise guy with the bed-head boasts 1,812 vertices / 1790 faces

This goes on top of the 15,836 faces sent to render the environment (hardly any of this actually intersects the viewport) using 7 calls to glDrawElements.

I don’t know about textured scenes – you can do a lot without textures (in fact, a lot more than what is demonstrated here). For now here’s a quick break down of how this renders:

  • 36.3% rendering the terrain
  • 12.5% rendering actors (frame interpolation doesn’t generate overheads in this particular case since I got the actors to pose for the pic)
  • 19% hit testing to keep the camera somewhere reasonable (oh no!)
  • 5% running game logic
  • 10% (!) refreshing the text box (this gets done quite often just to display the frame rate…)

I’m not worried yet about the terrain rendering time – I might have to eventually. Hit testing overheads are to go away altogether (not the hit testing, just overheads). I’m not planning yet on using distance based LOD – overall, I hope these stats make you feel that it’s possible to run at frame rate with detailed geometry on an iPod Touch, and if doesn’t, let’s wait until we get better screenshots :)

Here’s a quick benchmark (setup: 27551 faces; method: GLDrawElements, 295 calls; quartz-core overlay: yes; first person view, DOF 60 metres; no textures; GL-ES1.1; iTouch 8GB on OS 3.1.2).

  • lights:1, z-buffer:yes, smooth shaded; fog (quickest) – 10.0 FPS
  • without lighting: 13.5 FPS
  • with 2 lights: 7.5 FPS
  • disable linear fog: 10.3 FPS
  • reduce DOF to 30 metres (14614 faces, 126 units): 15.0 FPS (15096 faces, 131 units).
  • switch off smooth shading: 11 FPS
  • disable GL_COLOR_MATERIAL: 10.72 FPS

More about this test

In each case, I have varied only one parameter.

I like this test because it is testing an actual game environment. I’ll need to post a couple of pictures to make this clear. These results are actually pretty good considering the amount of geometry involved. Also worth noting:

  • There are serious overlaps in the geometry – we’re hit testing the same pixels over and over
  • The test is running 30 NPCs (not showing on the screen in this scene) with game logic.
  • Debug / Release mode makes little difference; once the game logic is optimized, it should represent a small overhead and our GL hardware doesn’t ‘switch to debug mode’ with the IDE :)

I’ve been spending time away from coding for a while as I was modeling actors and props. Now I’m in for another round of optimizations.

At this point, my stage includes actors (about 8-20 on-screen, 100 on stage), tiles (around 150) and props.

I am running somewhere between 5.7 and 8 fps. Here’s a quick breakdown of the game loop

  • 75% on the rendering loop
    • 33% rendering the terrain (terrain geometry is basic)
      • 11% on glDrawElements
    • 22% rendering actors
      • 10% frame interpolation
      • 2% on glDrawElements
    • 10% rendering props
      • 8% on glDrawElements
  • 25% on running game logic

The terrain is very simple, but I draw each tile with a call to glDrawElements. On overall processor usage, I get the following:

  • 20% on glDrawElements
  • 20% on msgSend
  • 5.6% for keyframe interpolation

Since I’ve also enabled a light source with smooth shading, I’ve also run a couple of tests to check how this affects frame rate. Given overheads everywhere else, the difference can be neglected.

Optimizing Terrain Rendering

If I tried VBOs at this stage, I think the result might be disappointing. Instead, what I want to do is cache large tiles including all terrain and props. Not only this means calling glDrawElements much less, also, it will remove all kinds of overheads associated with drawing terrain and props. Caching large tiles will use RAM and isn’t completely straightforward (plus we still need to find time to build the ‘super-tiles’ before caching them); I think it’s worth a try.

Test results

  • Cache 15×15 tiles, 8~10 FPS (vertices: 3817 faces: 4868)
  • Cache 25×15 tiles, 6.2~8.3 FPS (vertices: 11654; faces: 15054)

Now let’s see how this changes processing overhead distributions:

  • 58% on rendering (- 22)
    • 9.5% rendering terrain and props (-33)
      • 9.5% on glDrawElements
    • 46% >> rendering actors (+24)
      • 20% >> frame interpolation (+10)
      • 5% >> on glDrawElements (+3)
  • 41% >> on running game logic (+16%)

Frame interpolation

The next obvious step is to get actor rendering to run faster. If I disable frame interpolation altogether, I run at 11-12 fps. Sure I can’t just disable frame interpolation.

I can cache frames, unfortunately a simple caching approach will find it’s limit quickly, causing any of the following:

  • More work to design and execute models and animations
  • Drastic limits on the number of actor types on stage at any time.
  • Busting live memory

So instead of caching frames, can I make frame interpolation faster? Here’s a quick list of what I changed:

  • A FrameDescriptor class now stores pre-calculated weights and keyframe references – instead of looking up the right keyframes to interpolate from every time
  • Instead of allocating/deallocating memory for frame geometry, always use the same object/memory allocation as target for interpolation result
  • The core interpolation routine has been optimized by replacing external array accesses (something like foo.coordinates[index] ) by pointer accesses.

Herding CPU time

The new profile is as follows:

Now let’s see how this changes processing overhead distributions:

  • 38% on rendering (- 20)
    • 15% rendering terrain and props (-33)
      • 9.5% on glDrawElements
    • 19% >> rendering actors (-27)
      • 10% >> frame interpolation (-10)
      • 7% >> on glDrawElements (+2)
  • 61% >> on running game logic – at the moment, that’s evaluating decision for 100… sheep (+20%).

This runs at 10~11 fps.

In the mean-time, I’m still running logic for 100 NPCs. If I reduce that to 50, I get rates between 17 and 22 fps. But…

SuperTiles?

My first step was to optimize terrain rendering. That surely goes much faster in a way but…

Adding tiles and props to create large chunks of terrains is one thing. In my original test, I could render pretty large chunks without generating a significant overhead, except that…

  • Getting a large enough terrain at a low viewing angle (45/30 degrees) just requires rendering so many tiles
  • Optimizing this requires… well… a different approach.

In other words I found that my rendering slowed down to a crawl again after moving the SuperTiles thing from proof of concept to something more accurate.

Part of my problem was that I was using very small tiles, inherited from a 2D (!) version of my engine designed to support 32×32 pixel tiles.

I could make the tiles larger, but then other constraints (1 prop per tile) would start affecting artistic design adversely so…

…finally I dropped the grid, and I’m now using a quite different approach.

To be continued :)

Note: this article isn’t a benchmark. This article investigates GL-ES rendering performance in a running game prototype (with game logic running, not just GL rendering).

Today I’ve tested rendering performance with a simple character – ‘hit-man’ (230 vertices, 206 faces):

  • idle animation
  • walk cycle
  • hit animation

I use indexed faces (GLDrawElements) without VBOs (glVertexPointer). I’m not sure about using VBOs for actors because I interpolate animation frames on the fly – if I want to use VBOs, I’ll have to worry about how many mesh duplicates I can buffer, when to release them and so forth…

I animated a 100 actors this way, and it wasn’t fast – not hopelessly crawling like when I added game logic for the same 100 actors – just slow. So I did the obvious – added a boundary check to render only actors visible on-screen. That brought back an acceptable frame rate right away, without the need for further optimizations. The frame rate is still about OK with 20 actors on-screen.

This came as a surprise since my terrain rendering (no VBOs either) generates the highest overhead in my iPhone app. Here’s a quick breakdown of processor usage (rendering related only):

  • 40% – GLDrawElements for terrain (300 tiles, about 18400 faces)
  • 20% – Frame interpolation
  • 3.5% – GLDrawElements for actors (20-25 on-screen, about 4000-5000 faces )
  • (drawing props and doing other things)

Admittedly, my tiles are heavy. But frankly, it doesn’t add up – Getting 4 times more actors on-screen would just bring up drawing cost to 15%(1) (never mind frame interpolation), so still much less than tile rendering on the side of GL performance.

An experiment

I had a feeling that drawing large triangles took much longer than drawing small ones – so I iterated with basic square tiles (2 faces, 4 vertices) – here’s the result:

  • 9% – GLDrawElements for terrain (300 tiles, about 18400 faces)
  • 34% – Frame interpolation
  • 7.6% – GLDrawElements for actors (20-25 on-screen, about 4000-5000 faces )
  • (drawing props and doing other things)

In this configuration the game runs all nice and smooth – it’s not just that the tiles had many faces – faces for a single tile covered each other as well.

Conclusion

Rendering more pixels slows down rendering more than rendering more faces – With an isometric view, simple tiles and detailed characters can be a good combination.

(1) The way I evaluate this is so so – multiplying percentages this way isn’t maths

If you are using Blender for your 3D models, then you’ll want to export them to a format that OpenGL ES can understand. Maybe you just want to extract the data from Blender and load it into another format than just ‘raw vertex arrays’ – either way at the moment this is a little bit of a pain because there’s nothing like a standard script (see this article for roundabout solutions).

I’m working on a nice and nicer script for doing that. Today I’m just posting the code for a very simple script – after playing a little bit with this, maybe you’ll want to write your own export script (link to blender wiki-book), not because it’s easy with a little know-how, but because that will give you flexibility at low cost.

Outline

  • I export binary data from Blender using Python. It’s easy to write scripts that do that, especially because Blender has an interactive Python console and a data browser, so we can explore the structure underlying a scene dynamically and pick what we need. Other solutions tend to rely on parsing an *.obj or *.vrml file generated by a standard export script. This gives no control over what gets exported, and I believe parsing is harder than just exporting ad-hoc binary data and reading it back into your iPhone app.
  • I import the binary data using trivial stdio C functions. That is also easy – just check the code.
  • Writing/reading binary data may require running the Python script on your Apple Mac. I believe the ‘struct’ python class/module just writes using the system’s default binary types. So for example if you work with a graphic designer and for some reason they run Blender on a PC, you might get garbled output.

Here’s my python script – that needs to go in the blender scripts folder (see below):

#!BPY
# my_export_script.py
# this writes out the number of triangles in the
# model as an int, then writes out each coordinate for each vertex
# for each triangle. Indexed data is readily available from Blender
# and would be way more efficient.
""" Registration info for Blender menus:
Name: 'Test for simple mesh export2'
Blender: 249
Group: 'Export'
Tip: 'Export selected mesh for a test'
"""

import Blender
import bpy
import struct

def write_face_vertices(face,out):
	k=face.verts
	write_vertex(k[0],out)
	write_vertex(k[1],out)
	write_vertex(k[2],out)
	write_vertex(k[2],out)
	write_vertex(k[3],out)
	write_vertex(k[0],out)

def write_vertex(k,out):
	out.write(struct.pack('f',k.co.x))
	out.write(struct.pack('f',k.co.y))
	out.write(struct.pack('f',k.co.z))	

def write_obj(filepath):
	out = open(filepath, 'wb')
	sce = bpy.data.scenes.active
	ob = sce.objects.active
	mesh = ob.getData(mesh=1)
	out.write(struct.pack('i',len(mesh.faces)*2))
	#out.write('face_count %i\n' % (len(mesh.faces)))
	for face in mesh.faces:
		write_face_vertices(face,out)
	out.close()

Blender.Window.FileSelector(write_obj, "Export")

Now, here’s the class that reads the data – that goes directly into your project and requires nothing – well yes, you’d have to write your own header file for it :).

// RawMesh.m loads raw mesh data from the main resource bundle.

// This is where files end up when you build after

// adding them to your XCode project
// triangleCount is an int; vertexArray is a float*
// You could probably make vertexArray a float[] (a C array, not NSArray)
// and avoid the malloc/free worry doll.

#import “RawMesh.h”

#define VERTICES_PER_TRIANGLE 9

@implementation RawMesh

@synthesize triangleCount,vertexArray;

-(id)initFromResourceFile:(NSString*) fname{

if (self=[super init]) {

// get the file path from main resource bundle

NSBundle *mainBundle = [NSBundle mainBundle];

// rglmd=raw gl mesh data

NSString* path = [mainBundle pathForResource:fname ofType:@"rglmd"];

FILE * fh = fopen([path UTF8String], “rglmd”);

// determine the number of faces

fread(&triangleCount, sizeof(int), 1, fh);

NSLog(@”FACES:%i”,triangleCount);

vertexArray= malloc(sizeof(float)*triangleCount*VERTICES_PER_TRIANGLE);

fread(vertexArray, sizeof(float),triangleCount*VERTICES_PER_TRIANGLE, fh);

}return self;

}

-(void)dealloc{

[super dealloc];

free(vertexArray);

}

@end

Finally, here’s the code that renders this using GL-ES:

RawMesh* mesh=[[RawMesh alloc]initFromResourceFile:filename];
glVertexPointer(3, GL_FLOAT, 0, mesh.vertexArray);
// let the gl know that we want to use the vertex array.
glEnableClientState(GL_VERTEX_ARRAY);
// since the script didn't import color or material, just use anything
glColor4f(1.0f,0.6f,0.0f,0.0f);
// does this need an explanation?
glDrawArrays(GL_TRIANGLES, 0, mesh.triangleCount*3);

I hope this can help you. I’d like to comment more on the above (and please keep in mind it’s just a basic script to get started – I know there’s no animation, no materials, not even normals).

One more thing: on Mac OSx, you need to dig blender.app and look inside it to find Blender’s script folder. Because this is further buried down in a (hidden) dot file this can be a bit of a pain. But the blender wiki book (see my previous article) explains how to do it.

Here you are. I’ll improve this article. I didn’t want to delay publishing this code because many iPhone devs, like me, come from other languages and other platforms, so Python and C stdio can seem a little intimidating – collecting the information to write this misery of a scriptlet took me hours, now here it is.

This tutorial/quick reference explains the steps to setup lighting in OpenGL-ES 1.1 (may be compatible with OpenGL-ES 2.0).

Lighting requires that you prepare your models for lighting first, i.e. set/update material and provide per face/per vertex normals

1. Enable lighting; optionally, enable per vertex/per face color and smooth
shading.

glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glShadeModel(GL_SMOOTH);

2. Enable up to 8 light sources:

glEnable(GL_LIGHTn) // (e.g: GL_LIGHT0)

3. Configure your light sources:

Common to all light types, set light color for ambient, diffuse and specular
light components. Ambient lets a light illuminate every point in a scene, diffuse lets a light illuminate objects around it and specular adds a ‘shiny’ spot to your models.

glLightfv(GL_LIGHTn, GL_AMBIENT, color4f );
glLightfv(GL_LIGHTn, GL_DIFFUSE, color4f );
glLightfv(GL_LIGHTn, GL_SPECULAR, color4f );

note: color4f is array of floats with rgba, eg: {1,0,0,1} is red.

3a. Directional or positional light

glLightfv(GL_LIGHTn, GL_POSITION, vector4f );

note: vector4f is x,y,z + w component
w=0 to create a directional light (x,y,z is the light direction) like the sun
w=1 to create a positional light like a fireball*.

3b. Spotlight

glLightfv(GL_LIGHTn, GL_POSITION, vector4f );
glLightfv(GL_LIGHTn, GL_SPOT_DIRECTION, vector3f );
glLightf(GL_LIGHTn, GL_SPOT_CUTOFF, angle); // angle is 0 to 180
glLightf(GL_LIGHTn, GL_SPOT_EXPONENT, exp); // exponent is 0 to 128

note: high exponent values make the light stronger at the middle of the
light cone.

4. Attenuation

Attenuation makes the power of a light source fade when the model is further from the light source. May slow down rendering.

glLightf(GL_LIGHTn, attenuation, value)

where attenuation is one of:

GL_CONSTANT_ATTENUATION
GL_LINEAR_ATTENUATION
GL_QUADRATIC_ATTENUATION

(*) that won’t actually draw a fireball :), just the lighting effect caused by something like a fireball.

Last time we looked at drawing triangles with the GLES2Sample project (you can get this from here). Let’s modify this project a little so we can do interesting things.

First problem is that the ‘model’ is spinning. This is done using:

glRotatef(3.0f, 0.0f, 0.0f, 1.0f);

You can change 3.0f to 0.0f or comment out this line of code to test. Here’s the code fragment, still within ES1Renderer:

// from ES1Renderer.m in GLES2Sample
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
 // left, right, bottom, top
glOrthof(-10.0f, 10.0f, -15.0f, 15.0f, -1.0f, 1.0f);
 glMatrixMode(GL_MODELVIEW);
 // this causes the square to rotate
 glRotatef(3.0f, 0.0f, 0.0f, 1.0f);

This code is puzzling. How does it work? What’s a matrix anyway? Can we use it to setup an isometric view? Yes! We want to tilt the ‘model’ 45 degrees left and 60 degrees right. tilt=rotate, so let’s uncomment glRotatef(!).


glRotatef(angle,x,y,z)

Where:
- angle is the angle we rotate by.
- x,y,z is a vector to rotate around.

For example:

glRotatef(60,1,0,0) rotates your model around the X axis – like holding a skewer in both hands and making it spin.
glRotatef(45,0,1,0) rotates your model around the Y axis – like a horse on a carrousel.

// updated to try creating an isometric view
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// left, right, bottom, top
glOrthof(-10.0f, 10.0f, -15.0f, 15.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
// this is trying to setup the isometric view.
glRotatef(60,1,0,0);
glRotatef(45,0,1,0);

This should give us an isometric view – tilted so we look from up and from the side. Run this code.
Well… actually this gives me headache. What we really want is rotate just once, then stop. There are several ways to do this:

1 – Since it’s animating there’s probably a time-loop running somewhere, s o we could stop the time-loop. Since we’re making a game, we probably want to leave the time-loop running.
2 – We could use a latch to rotate only once within (void)render:, so that the first time the function rotates, then stops rotating forever. That’s pretty dumb. Not elegant.
3 – Is there a way we can just specify the angle at every frame, rather than rotate again at every frame? Yes.

glLoadIdentity() happens to be used to clear the rotation of the ‘model’.

Given the fact, it seems that we are already clearing the rotation at every frame glLoadIdentity is already there! Let’s call this function again anyway, just before we call glRotatef()

glMatrixMode(GL_PROJECTION);
glLoadIdentity();  // this call was already there.
// left, right, bottom, top
glOrthof(-10.0f, 10.0f, -15.0f, 15.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
// this causes the square to rotate
glLoadIdentity();  // hard headed: calling glLoadIdentity again.
glRotatef(60,1,0,0);
glRotatef(45,0,0,1);

Run this. Yes – now that works. Notice that i swapped the y for the z coordinate to make it look right. This is easy to get wrong at the beginning.

*Why* does it work?

1 – GL doesn’t know about your ‘model’. It just draws stuff. That would be the first answer to the question ‘how does it know how to rotate that particular model’. It doesn’t. We’re not rotating the model, we’re rotating the view. So we rotate the view first, and draw next, and it seems like we have rotate the model.
2 – GL retains all state assigned to it. glLoadIdentity() actually affects a target defined by glMatrixMode(). Notice that there are two calls to glMatrixMode:

glMatrixMode(GL_PROJECTION) – set the so called projection matrix as target to transformations such as glRotatef, glOrthof etc…
glMatrixMode(GL_MODELVIEW) – set the so called model view matrix as target to transformations.

The first call to glLoadIdentity() actually resets the projection. This implies that before adding a second call to glLoadIdentity after glMatrixMode(GL_MODELVIEW), the model view matrix was not reset between two invocations of render: that explains the animated rotation.

The projection matrix defines the ’3D style’ such as isometric (no distortion) and perspective (further objects are smaller).
The model view matrix defines the rotation of the camera or ‘the whole world’.

The aim of this tutorial is to show how to get started with OpenGL ES for iPhone/iTouch; OpenGL ES 2.0 is only available on the iPhone 3GS, so this article focuses on 1.1.

The article is based on the GLES2Sample project from the Apple iPhone dev library. This project is ideal as a starting point for a 3D app/game for several reasons:

  • It is compatible with OpenGL-ES 1.1/2.0 and can choose the right version at runtime.
  • It provides a run loop, and will select the best strategy to setup a run loop according to whatever version of iPhone OS is running on a target.
  • We can ignore the boiler plate and start playing (or indeed, developing) right away.

In short, OpenGL-ES on iPhone requires setting up a special UIView and a run loop. You’ll need a good reason to modify the default setup. OpenGL rendering itself is all done within the ES1Renderer and ES2Renderer classes.

If you want to learn more about the boiler plate, it’s in the example (and there is apple documentation to explain the boiler plate) If you want to know how to get started with basic OpenGL commands, live in ignorance and bliss and learn more and more about OpenGL – ES, this article is just for you.

  1. Download and open the GLES2Sample project from the Apple iPhone dev library.
  2. In EAGLView.m comment out the following line:
    renderer = [[ES2Renderer alloc] init];
    This makes sure we are only using the ES1 renderer (otherwise changes described in this article will have no effect). An alternative could be to set your device version to 3.0
  3. Open ES1Renderer.m
  4. Search for - (void) render
  5. Notice the const GLfloat and const GLubyte arrays. The first array contain (2D) coordinates (vertices). The second array contains colors in RGBA format. Skip to glVertexPointer (a few lines down, same function)
    - glVertexPointer passes the array of vertices to the gl.
    - glEnableClientState(GL_VERTEX_ARRAY) tells the GL that you indeed want to use the passed vertex data.
    - glColorPointer passes colors (to be associated to vertices) to the GL.
    - glEnableClientState(GL_COLOR_ARRAY) tells GL that you indeed want to use the passed colors.
    - glDrawArrays tells gl to use the data you passed/enabled to draw triangles.

Here’s the code fragment anyway:

const GLfloat squareVertices[] = {
-0.5f, -0.5f,
0.5f,  -0.5f,
-0.5f,  0.5f,
0.5f,   0.5f,
};
const GLubyte squareColors[] = {
255, 255,   0, 255,
0,   255, 255, 255,
0,     0,   0,   0,
255,   0, 255, 255,
};
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();	// this clears the projection
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();	// this clears the model transform
glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
glRotatef(60.0f, 1.0f, 0.0f, 0.0f); // rotate around x axis
glRotatef(45.0f, 0.0f, 0.0f, 1.0f); // rotate around z axis

glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
glEnableClientState(GL_COLOR_ARRAY);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

NOTE: The above code fragment is reproduced with Apple’s permission (as per ES1Renderer.m license in the ES2Example)

This is pretty much how the GL works. Some GL calls are used to pass data, other GL calls are used to effect commands on this data.

Let’s have a look at the above functions in detail:

1) in glVertexPointer(2,GL_FLOAT,0,squareVertices), parameters are:
- 2 is the number of coordinates per vertex. that could be 2 or 3.
- GL_FLOAT is the vertex format. I think float is often fine – modern CPUs/GPUs are kind of optimized for float versus integer calculations, not intuitive but maybe true. floats are easy to use.
- 0 is the stride. Let’s keep this to zero to save explaining what is stride.
- squareVertices is your vertices as defined in the array.
2) in glColorPointer(4,GL_UNSIGNED_BYTE,0,squareColors),
- 4 is colors per vertex, which is red, green, blue + alpha
- GL_UNSIGNED_BYTE is color components from zero to 255. You can have floating point colors in the range [0.0-1.0] if you prefer. I don’t know which is faster. maybe the unsigned byte version.
3) in glDrawArrays(GL_TRIANGLE_STRIP,0,4)
- GL_TRIANGLE_STRIP means draw triangles in a sequence, reusing
(apparently) 2 vertices every time. You can use GL_TRIANGLES instead. not as efficient, but to make things by hand, GL_TRIANGLES is logical – you want 2 triangles, you pass 6 vertices.
- 0 is the index of the first vertex you want to draw from.
- 4 is the index of the last vertex you want to use.

Usually to do some low level 3D rendering you draw triangles. GL also lets you draw lines and vertices.

OpenGL is the IT industry standard for drawing triangles, lines and points, and a lot more all the way down to the best realtime 3D games around.

OpenGL-ES1 is ‘OpenGL light’ suitable for mobile platforms such as the iPhone.

ES2 is the high end version of OpenGL-ES. Available on iPhone 3GS and maybe other platforms. ES2 has shaders, ES1 doesn’t.

Do I need to know OpenGL/ES to make (mobile) 3D games?

Not really, I don’t think so. You can learn a game engine instead, such as Unity, SIO2, Panda3D, Irrlich, Ogre.

So why would I learn OpenGL/ES?

OpenGL has been around for more than 15 years. If you want to become a 3D graphics programmer, it’s probably a good idea to learn both OpenGL and a game engine.

OpenGL is low level. Compared to using a game engine, knowing how to use the GL lets you be creative in different ways.

OpenGL is a little weird, but a lot of it is actually simple. Drawing shapes is a low level, yet simple concept. If you like ‘drawing and programming in 3D’, then OpenGL could be a better choice than a high level game engine that encourages/constrains you to just get your models from a 3D artist and fire animation commands.

OpenGL is the only API that maps directly to modern graphics hardware. Some people use the GL to do weird stuff that’s not really related to graphics, just because GPUs are fast and full of maths.

I’ve been hesitating a lot between re-learning the GL and learning a game engine. I’ll probably do both in the end but for now, because I don’t need serious physics, probably can’t afford having a lot of textures and I quite like drawing, I thought OpenGL may be a reasonable choice. Also, I’m currently creating a very high level scripting API to a 3D platform, so I like to look at things from the other end in my spare time.