As I mentioned in an earlier post, OpenGL ES 2.x replaces the fixed function pipeline of OpenGL ES 1.x with a programmable pipeline. This means that you have to write a vertex shader program and a fragment shader program that perform the operations that the fixed function pipeline used to do. What you gain is an unprecedented level of flexibility as you are no longer restricted to hardwired calculations in the GPU.
The vertex shader processes polygon vertices and the fragment shader processes rasterized pixels. There are lots of tutorials and documentation on the basics of shaders, so I won't go into all the details. Suffice it to say that the vertex shader program is run once for each vertex in the polygon and the fragment shader is run for each polygon pixel to be drawn. Instead, I will show you a couple of examples of extremely basic vertex shaders.
Each polygon vertex has a position in 3D space. One of the primary responsibilities of the vertex shader is to project the vertex position into 2D space. By default, the OpenGL 2D coordinate system of the iPhone looks like this:

This is an orthographic projection, which means there is no sense of perspective and the depth component of the vertex position is more or less ignored. For a (non-degenerate) polygon to be visible in the default coordinate system, at least one of its vertices would have to have an XY position within the range shown in the image.
The following vertex shader simply copies the position attribute of the vertex to the gl_Position variable, which is the predefined vertex position that will be used by the hardware when the polygon is rasterized and drawn.
attribute vec4 position; void main() { gl_Position = position; }
This is probably really the simplest possible vertex shader you can write without hardcoding the position. However, you rarely want to use the default coordinate system, because it has an awkward aspect ratio and no perspective projection.
Let's say we want to create a 2D game where the coordinate system matches the resolution of the iPhone screen, which is 320x480 pixels. We wouldn't have to worry about perspective in this case, since we're not going to do 3D, so it's ok to still have an orthographic projection. Basically, we want a coordinate system that is more common when dealing with 2D pixel graphics in general, where the upper left corner is the origin. Here's an illustration of the coordinate system we want:

To accomplish this, we would have to create a view projection matrix that could be used to project the vertex position from our coordinate system to the default coordinate system. To perform the projection transformation in the vertex shader, we simply multiply the vertex position with the view projection matrix:
attribute vec4 position; uniform mat4 viewProjectionMatrix; void main() { gl_Position = viewProjectionMatrix * position; }
As you may have noticed, the position is represented by a vector with 4 components instead of 3. This is because the vector has to be expressed in homogenous coordinates for affine matrix transformations to be possible. If this makes no sense to you, it is safe (most of the time) to just accept that there has to be a 1 in the fourth component of the vector.
The last remaining piece of the puzzle is to create the view projection matrix, so that it can be fed to the vertex shader and bound to the viewProjectionMatrix variable. Those of you who have a perverted math fetish may go ahead and calculate the transformation matrix manually, but the rest of us will use a preexisting math library function instead. I highly recommend the free GLGX library for matrix and vector operations. It was created specifically for use with OpenGL. Moreover, GLGX is heavily inspired by the DirectX utility library D3DX. For almost every function in D3DX, there is a corresponding GLGX function.
Using GLGX, you would create the view projection matrix by using the following function call:
GLGXMatrixOrthoOffCenter2D(&viewProjectionMatrix, 0.0, 320.0, 480.0, 0.0);
I'm too lazy to go into how to bind the C matrix to the matrix variable in the vertex shader, so I'll leave it as an exercise to the reader for now.
People at work (and elsewhere) sometimes ask me if I'm a C or a Java programmer. Sometimes they even assume that I don't know how to write C code, based on the product written in Java that I am currently spending most of my time on. This annoys me a great deal.
Before I was asked these questions I never even considered people to be programmers of one specific language. It would be like asking a carpenter if he (or she) is a hammer carpenter or a screwdriver carpenter. In order to be a good developer, or a good carpenter for that matter, you should really know how to use more than one tool. You don't want to be a one trick pony.
It's important to broaden your horizons and see what kind of different languages and paradigms are out there. Learning a new language can really put things into perspective and make you think about programming in a new way.
For example, learning REBOL was the best thing I ever did, because it taught me new concepts that I didn't even know existed. I have been able to use this knowledge in other languages where these concepts may not have been obvious to me otherwise.
I have tried somewhere around 20 different programming languages and I encourage you to do the same, if you haven't already. It will be worth it.
Hardware accelerated graphics code is hard to debug. This is a fact. I know that from experience. Some of the most frustrating bugs I have ever encountered have been 3D related.
Over the years I have learned that catching the bugs early saves a lot of time and work in the end. It may also prevent you from going insane. I value my sanity, so I have decided to make my debug build help me as much as possible.
Apple recommends that you check the OpenGL error flag often and I concur. However, calling glGetError() too often results in a performance penalty. For this reason, I only check for GL errors in my debug build, where I actually check for errors after every single GL function call. That's how dedicated I am to squashing those pesky little bugs! (I like the movie Starship Troopers because it's all about bug killing.)
To be more specific, I have wrapped glGetError() in a function called checkGL() which programmatically triggers a breakpoint whenever an error has been detected.
Here's some code that you may adopt as your own, as long as you don't blame me if something goes wrong. Yes, this is a disclaimer. If you use any of my code, you are doing so at your own risk, I take no responsibility whatsoever. That said, I don't think there are any problems with this code...
Here's the header file (GLError.h):
#ifdef __cplusplus extern "C" { #endif #if TARGET_IPHONE_SIMULATOR #define BREAKPOINT __asm__ volatile ("int3"); #else #define BREAKPOINT __asm__ volatile ("bkpt 1") #endif #ifdef DEBUG_OPENGL #define CHECK_GL checkGL() void checkGL(void); #else #define CHECK_GL #endif #ifdef __cplusplus } #endif
And here's the implementation file (GLError.m):
#import "GLError.h" #import <OpenGLES/ES2/gl.h> #import <OpenGLES/ES2/glext.h> void checkGL(void) { GLenum err; if (err = glGetError()) { switch (err) { case GL_INVALID_ENUM: NSLog(@"OPENGL ERROR: GL_INVALID_ENUM"); BREAKPOINT; break; case GL_INVALID_VALUE: NSLog(@"OPENGL ERROR: GL_INVALID_VALUE"); BREAKPOINT; break; case GL_INVALID_OPERATION: NSLog(@"OPENGL ERROR: GL_INVALID_OPERATION"); BREAKPOINT; break; case GL_OUT_OF_MEMORY: NSLog(@"OPENGL ERROR: GL_OUT_OF_MEMORY"); BREAKPOINT; break; default: NSLog(@"OPENGL ERROR: 0x%x", err); BREAKPOINT; break; } } }
That's it for today. Over and cout. (pathetic C++ joke)
I was surprised to find that OpenGL ES 2.0 does not have support for a fixed function pipeline (FFP) at all. You have to use shaders. Obviously, shaders are powerful and give you a level of flexibility that is not possible with a FFP. Unfortunately, it also means that you have to implement all the calculations of the FFP yourself, even when all you really need are the simple features that the standard FFP provides out of the box in OpenGL ES 1.x.
Initially, this means more work to get something to show up on the screen, but once you have implemented a basic shader, you will have a template to start with for future shaders, so it's really only a one-shot effort.
I wrote my first shader in a sort of shader assembly language back in 2001. Since then various high level shader languages have appeared, which makes writing shaders much less of a pain in the nether regions. I believe HLSL and GLSL are the most well known shader languages and unless I am mistaken the OpenGL ES has its own variation of GLSL called OpenGL ES Shading Language. I highly recommend learning about the shader assembly instructions even if you are only going to use a high level language, because it gives you a better understanding of the fundamental functionality of the shader hardware.
While I have written a lot of shaders over the last few years, I have exclusively been using HLSL, because I have been using the XNA framework on the Xbox 360. The underlying concepts should essentially be the same so hopefully I will be able to pick up the ES shading language quickly. As soon as I have a working example shader, I will post my findings here.
Hello, my name is Martin Johannesson and this is my home on the web. I live in Stockholm, Sweden, where I work as a software engineer at a software company.
Ever since I was a kid and discovered the art of programming on my
C64,
I've been tinkering with my own little software projects and experiments.
This site is one such experiment.
more...