View All Posts
read
Want to keep up to date with the latest posts and videos? Subscribe to the newsletter
HELP SUPPORT MY WORK: If you're feeling flush then please stop by Patreon Or you can make a one off donation via ko-fi
#CODING #FRAGMENT SHADER #GAME DEVELOPMENT #IPHONE DEVELOPMENT #OPENGL #OPENGLES 20 #PROGRAMMING #TUTORIALS #VERTEX SHADER

You can find an index to the ported tutorials here.

I recently created a new game for the iPhone (Tanks! Mayhem) and the Palm Pre/Pixie (Tanks!) as part of an effort to learn OpenGLES 2.0. I’d already become familiar with ES 1.0 using it in a previous game but felt that for the effect I wanted to achieve in the new game ES2.0 was the way to go.

OpenGLES 2.0 introduces the idea of a programmable graphics pipeline. This basically puts you in charge of writing the code that is going to generate your graphics - very powerful, very generic, very overwhelming for someone getting started.

To get going I purchased the Blue Book (OpenGLES 2.0 programming guide) - which is great and I thoroughly recommend it - but one of the things that I found I really missed were some simple “this is how you’d do this thing you did in 1.0 in 2.0”. Lots of books give you a complete implementation of 1.0 written as a shader which is interesting, but not much help for someone trying to get started.

When I was starting out learning ES 1.0 I found Jeff Lamarche’s excellent set of blog posts where he ported the NeHe tutorials over to the iPhone.

I’m going attempt to do the same for ES2.0 and port the tutorials over to ES2.0 on the iPhone - I’m going to assume that anyone reading this is familiar with ES1.0 - if you’re not then why not run through his tutorials…

Before we get started we’re going to need some ground work. As I said, ES2.0 introduces us to a programmable pipeline, there are two areas of the pipeline that we can program, the coordinate processing, and the setting of pixel colours. In ES2.0 the code you run for these two areas is called a “vertex shader” and a “fragment shader”. These two bits of code combine to make a “program”.

As inputs, these programs take:

  • vertex attributes - consider these as the arrays you would normally pass into functions like glVertexPointer, glTexCoordPointer, glColorPointer etc...
  • uniforms - I think of these like constants

Only the vertex shader part of the program gets access to the values from the vertex attributes. The vertex shader is run for each array index of the arrays you pass in for the vertex attributes. It also has access to the uniform values. The vertex shader is responsible for writing a value to the gl_Position variable - this variable tells OpenGL where to do it’s drawing. So for example if you wanted to draw a triangle you would create an array with three vertices specifying where you want your triangles to be drawn. You’d put that array into a vertex attribute and then issue the glDrawArrays with GL_TRIANGLES as the drawing type. Your vetex shader would get called for each vertice in the array and would take the coordinates and copy them into the gl_Position variable.

OpenGL takes the coordinates you generated, applies any clipping, and then rasterises the triangle. For each pixel it generates during the rasterisation process the fragment shader is called to find out what color the pixel should be.

That’s what I think happens anyway…

The fragment shader has access to all the uniforms. It also has access to varying variables. Varying variables are values that can be set by the vertex shader to be passed along to the fragment shader. They are called varying because they vary from pixel to pixel. The reason for this is because OpenGL interpolates the value they contain between each vertice.

That’s probably as clear as mud. So on with the lessons…

Lesson 1 of the NeHe tutorials is all about getting set up to render. We’re going to use the standard xCode OpenGLES template. We’re also going to need a bunch of helper functions. I’m using the set of common files that come with the OpenGL ES2.0 programming guide. You can download them as part of the example code from here: http://www.opengles-book.com/downloads.html.

Tutorial 2

Lesson 2 is where the fun actually starts. We’re going to draw a white triangle and a sqaure on the screen - very exciting…

It’s at this point that I got stuck. where had glFrustrumf and glOrthof gone? How was I supposed to set up my projection and modelview matrices?

ES 2.0 makes no assumptions about how you are going to transform your coordinates, you may choose to use a projection and modelview matrix. You may choose some completely arbitrary combination of matrices. You may even not be transforming your coordinates at all using a matrix.

However, typically you will want to have a projection matrix and modelview matrix. The sample common code that comes with the OpenGL ES2.0 programming guide comes in really handy here. It has the missing frustrum and ortho functions along with a handy set of matrix functions. Using the functions we can set up out project and modelview matrices:

In your class file you’ll need three variables:

ESMatrix projection;
ESMatrix modelView;
ESMatrix mvp;

Projection will hold our projection matrix and modelView will hold our modelView matrix. To set these up we’ll use the following code - which should look very familiar:

const GLfloat zNear = 1, zFar = 600, fieldOfView = 40*M_PI/180.0;

esMatrixLoadIdentity(&projection);
GLfloat size = zNear * tanf(fieldOfView / 2.0); 
esFrustum(&projection, -size, size, -size / ((float) height / (float) width), size / ((float) height / (float) width), zNear, zFar); 

esMatrixLoadIdentity(&modelView);

glViewport(0, 0, width, height);  

This will set our project matrix up with a perspective matrix and our modelView matrix up with identity. To get our combined modelview project (mvp) matrix that we can use for transforming coordinates we’ll do:

esMatrixMultiply(&mvp, &modelView, &projection );

We now need our shader code. For this simple example we are going to be drawing triangles (in the original NeHe tutorial they draw a square as well - but ES is limited to triangles). This is pretty simple - our vertex shader will need to transform the input coordinates by our mvp matrix and our fragment shader just needs to set the pixels to whatever colour we choose. So here’s our vertex shader:

attribute vec4 position;
uniform mat4 mvp;

void main()
{
    gl_Position = mvp * position;
}

So, what’s going on here? We’ve got two inputs to the shader, a vertex attribute called position which is a vector and a uniform called mvp which is a 4x4 matrix. The position attribute will take our input vertex coordinates and the mvp matrix will be filled with the mvp matrix that we created from our modelview and projection matrices. And here is our fragment shader:

uniform lowp vec4 color;

void main()
{
 gl_FragColor = color;
}

Here we just have one input - a uniform that is a vector that we’ll load up with the colour we want to set our pixels to. To load these bits of code into the GPU we need to compile and link them. The first step is to compile the source code and attach the compiled shaders to a program object:

GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);

GLuint simpleProgram = glCreateProgram();
glAttachShader(simpleProgram, vertexShader);
glAttachShader(simpleProgram, fragmentShader);

We then need to bind our vertex attributes to specific locations. This will let us access them later. This step needs to be performed before the program is linked:

glBindAttribLocation(simpleProgram, ATTRIB_POSITION, "position");

ATTIB_POSITION is an arbitrary value that you choose in your own code. We can now link the program:

glLinkProgram(simpleProgram);

And finally we can get the locations of our uniforms.

uniformMvp=glGetUniformLocation(simpleProgram, "mvp");
uniformColour=glGetUniformLocation(simpleProgram, "colour");

We should now be fully setup and ready to draw! The following code draws a white triangle on the screen:

// vertices that we're going to draw
const GLfloat triVertices[] = { 
  0.0f, 1.0f, 0.0f, 
  -1.0f, -1.0f, 0.0f, 
  1.0f, -1.0f, 0.0f 
 }; 
// translate back into the screen by 6 units
esTranslatef(&modelView, 0.0f ,0.0f,-6.0f);
// tell the GPU we want to use our program
glUseProgram(simpleProgram);
// create our new mvp matrix
esMatrixMultiply(&mvp, &modelView, &projection );
// set the mvp uniform
glUniformMatrix4fv(uniformMvp, 1, GL_FALSE, (GLfloat*) &mvp.m[0][0] );
// set the colour uniform (r=1.0, g=1.0, b=1.0, a=1.0)
glUniform4f(uniformColour, 1.0, 1.0, 1.0, 1.0);
// set the position vertex attribute with our triangle's vertices
glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, false, 0, triVertices);
glEnableVertexAttribArray(ATTRIB_POSITION);
// and finally tell the GPU to draw our triangle!
glDrawArrays(GL_TRIANGLES, 0, 3);

That’s it - you’ve got a triangle on the screen. You can download the full source code for tutorial 2 from here. The important code is found in Tutorial2ViewController.m in the loadShaders, setupView and drawFrame methods. It will draw both a triangle and a square on the screen.

Tutorial 3

We’ve actually already covered part of tutorial 3 by adding a uniform to our fragment shader that lets you set the colour. If you uncomment the line just after the comment that says “// change the colour uniform if you want to have a different colour” then your square will come out in green. However, tutorial 3 is more about being able to change the colour at each vertex. How do we do this in ES2.0? This is where varying variables come in. Along with our position attribute we are going to add a colour attribute. The value of this will be assigned to a varying variable which will be passed into our fragment shader - the value of this variable will then be used to set the pixel value. Our vertex shader now looks like:

attribute vec4 position;
attribute vec4 colour;

uniform mat4 mvp;

varying lowp vec4 vColour;

void main()
{
 gl_Position = mvp * position;
 vColour = colour;
}

and our fragment shader like this:

varying lowp vec4 vColour;

void main()
{
 gl_FragColor = vColour;
}

In our code we will now need a set of colours for the vertices:

const GLFloat triColours[] ={
 1.0f, 0.0f, 0.0f, 1.0f,
 0.0f, 1.0f, 0.0f, 1.0f,
 0.0f, 0.0f, 1.0f, 1.0f,
}

And these will need to be set into our new colour attribute. Note that we no longer have a uniform called colour in our shaders.

// set the colour vertex attribute with our triangle's colours
glVertexAttribPointer(ATTRIB_COLOUR, 4, GL_FLOAT, false, 0, triColours);
glEnableVertexAttribArray(ATTRIB_COLOUR);

You can download the full source code for tutorial 3 from here. The important code is found in Tutorial3ViewController,m in the loadShaders, setupView and drawFrame methods. It will draw both a triangle and a square on the screen with different colours at each vertex.

Tutorial 4

For tutorial 4, just use the esRotate function from the esUtils code - apply it to your modelview matrix before you ceate the mvp matrix. e.g.

// translate up by 3 units
esTranslate(&modelView, 0.0f , 3.0f, 0.0f);
// rotate by 45 degrees around the z axis
esRotate(&modelView, 45.0, 0.0, 0.0, 1.0f);
// create our new mvp matrix
esMatrixMultiply(&mvp, &modelView, &projection );
// update the mvp uniform
glUniformMatrix4fv(uniformMvp, 1, GL_FALSE, (GLfloat*) &mvp.m[0][0] );

You can use either Tutorial 2 or 3 to play around with this.

Over the coming weeks I’ll port the rest of the tutorials - hopefully I’ll learn some more as I do it.

#CODING #FRAGMENT SHADER #GAME DEVELOPMENT #IPHONE DEVELOPMENT #OPENGL #OPENGLES 20 #PROGRAMMING #TUTORIALS #VERTEX SHADER

Related Posts

Creating an OpenGL game for both the Palm and iOS - I've made a triumphant return to the blogosphere with the release of my new game, 'Tanks!' developed for Palm Pre/Pixie and soon for iPhone devices. The game, an experiment to learn OpenGLES 2.0, is a testament to the feasibility of porting C++ or C code from iOS to Palm PDK, despite minor challenges with UI interaction and audio. So gear up, folks! Download 'Tanks!', step onto the battlefield and let the mayhem begin! Next stop on my coding odyssey - Android!
NeHe OpenGL tutorial 5 ported to ES2.0 - In this blog post, I delve into the specifics of porting code for drawing 3D shapes in ES2.0. I detail how to overcome the lack of depth buffer in the default OpenGL template and describe how to add rotation to objects by saving the model view matrix. This post offers a useful walkthrough, especially for those keen to deepen their understanding of 3D rendering nuances in ES2.0.
Getting started with Box2D - Just gave a enlightening talk on Box2D at the LiDG and have prepared a step-by-step walkthrough on creating a simple pinball game using the open sourced 2D physics engine! You'll learn the concepts of Body, Shape, Friction, etc. as well as how to build and run the Box2D engine for iPhone. By the end of the blog, you'll be equipped to create a basic working game in a couple of 100 lines of code! Don't forget to check out my slides and demos linked in the blog!
Augmented Reality on the iPhone - how to - Hey there tech enthusiasts! So, you used to rely on my old methods for employing augmented reality on an iPhone? Well, those days are past. With the release of iOS4, accessing the camera has become a breeze. Check out my latest blog post where I share the specially updated code that works seamlessly with iOS4.
Shoot3D for the iPad/iPhone - I'm thrilled to have introduced my first 3D game on the app store. The entire process served as a real learning curve, with some elements still appearing obscure, yet adding to the intrigue. The games section of the app store is known for its fierce competition, but challenging the status quo by doing something different from the regular utility apps is quite a welcome change. I'm generally proud of how the game has turned out and look forward to its reception. You can download the game on the app store.

Related Videos

Tanks! for the Palm Pre and Palm Pixie - Witness the successful porting of an iPhone app to a Palm Pre Plus in this intriguing video!
AR Sudoku Solver in Your Browser: TensorFlow & Image Processing Magic - Discover how to recreate a Sudoku app using browser APIs and ecosystem advancements, and explore the image processing pipeline technique for extracting Sudoku puzzles and solving them.
Laser Projected Asteroids: ESP32 Reinvents Classic Arcade Game! - Learn how to create a classic Arduino ESP32 Asteroids game with laser projection, including a tour of the system hardware and a look at its firmware and game engine. Watch a demonstration and comparison of fonts, as well as an overview of the audio output. Dive into the schematics and try out the game for yourself!
256 Shades of Grey – Adventures in Image Processing - In this enlightening video, I delve into the deep and fascinating world of image processing. Forget everything you thought you knew about pixels – they’re not squares or rectangles and they definitely aren’t discs. All pixels are, my friends, are point samples, each capturing brightness or color at a particular position. Curious to know how to manipulate them? I also unravel this mysterious tapestry, familiarizing you with the technicalities of grayscale, RGB, HSB, YUV, and a fleeting mention of CMYK. And if you think that's all, hold tight! Did you know we could apply Fourier transforms, akin to graphic equalisers used in audio, to our good old two-dimensional images? Strap in as I guide you through this potentially overwhelming realm with a pinch of humor, lots of simplicity, and oodles of practical examples.
Wio Terminal Audio Visualizer - Learn about porting an audio monitor to the WiiO terminal, featuring a built-in microphone, gyroscope, accelerometer, light sensor, and infrared emitter. Check out the simple base class used for the display and the efficient template class implementation to swap out between two different display libraries.
HELP SUPPORT MY WORK: If you're feeling flush then please stop by Patreon Or you can make a one off donation via ko-fi
Want to keep up to date with the latest posts and videos? Subscribe to the newsletter
Blog Logo

Chris Greening


Published

> Image

atomic14

A collection of slightly mad projects, instructive/educational videos, and generally interesting stuff. Building projects around the Arduino and ESP32 platforms - we'll be exploring AI, Computer Vision, Audio, 3D Printing - it may get a bit eclectic...

View All Posts