The previous lesson showed us how to set up the project and create a view. Now it’s time to actually draw something, and to keep it simple, why not draw a square? Drawing in OpenGL ES 2.0 may appear a little complicated, but it really isn’t once you understand how it works. So read carefully.
All drawing in OpenGL ES 2.0 is done with something called shaders, you have probably heard of them. They’re new in OpenGL ES 2.0 and once you understand what they are all about, I’m sure you’re going to love them. Shaders make drawing more powerful, flexible, and straight forward than before.
This project can be found at GitHub.
What is a shader?
A shader is an independent computer program executed on the GPU. To create one, you write the source code, compile it and finally link it. This is same procedure as when you build an iOS app, but the unusual thing about shaders in iOS is that compiling and linking is done at run time. The shader program source code consists of two separate files, the vertex shader source and the fragment shader source. Basically, the vertex shader handles geometry and the fragment shader handles color. These are compiled separately and then linked together to create a program. The shaders are written in a language called OpenGL ES 2.0 Shading Language which syntax is very similar to C, but it’s not C, it’s an independent language.
Create shader source files
First of all, we create two empty files to store the source code, “VertexShader.vsh” and “FragmentShader.fsh”. These should be added to the “Copy Bundle Resources” list in your targets “Build Phases”. Since they contain source code, you might have expected them to be added to the “Compile Sources” list, but that’s not the case! As stated above, the shader source code will be compiled at run time, so we want to access them like regular files.

Write vertex shader source
The vertex shader handles geometry. Open “VertexShader.vsh” and add the source code below. That’s all we need.
precision mediump float;
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
1. First we need to set the float precision. This just defines the default precision of all floats in the file.
2. Create an attribute named a_position. Simply put, an attribute is a way for us to input data to the shader, that’s enough to know for now. We use the prefix a_ to denote that it’s an attribute.
3. Create the main method. gl_Position is a built-in variable that needs to be set in the vertex shader.
Write fragment shader source
The fragment shader handles color. Open “FragmentShader.fsh” and add these lines:
precision mediump float;
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
1. Set default float precision.
2. The purpose of the vertex shader is to set the built-in variable gl_FragColor. Here, we’re setting a nice green color by using the built-in method vec4 that takes RGBA as arguments.
Compile vertex shader
So now when we have written the shader source code, we need to compile it. We’re starting by reading the source code into an NSString using standard Cocoa methods. Since OpenGL wants the source code as a plain CString, we need to convert it.
NSString *vertexShaderSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"VertexShader" ofType:@"vsh"] encoding:NSUTF8StringEncoding error:nil];
const char *vertexShaderSourceCString = [vertexShaderSource cStringUsingEncoding:NSUTF8StringEncoding];
Creating the vertex shader and compiling it is quite straight forward and self explaining. We’re just giving it the source and telling it to compile.
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSourceCString, NULL);
glCompileShader(vertexShader);
Compile fragment shader
The procedure for compiling the fragment shader is almost identical.
NSString *fragmentShaderSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"FragmentShader" ofType:@"fsh"] encoding:NSUTF8StringEncoding error:nil];
const char *fragmentShaderSourceCString = [fragmentShaderSource cStringUsingEncoding:NSUTF8StringEncoding];
Creating and compile the fragment shader.
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSourceCString, NULL);
glCompileShader(fragmentShader);
Create the program
When we have compiled the vertex and fragment shader, it’s time to link them together to create a program.
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
Finally, we need to define that this is the program that we want to use for drawing.
glUseProgram(program);
Define the geometry we want to draw
To do this, we have to define the corners of the square. Let’s just create a unit square centered around origo.
GLfloat square[] = {
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
0.5, 0.5};
We would now like to send the square geometry data to the shader. So how do we do that?
const char *aPositionCString = [@"a_position" cStringUsingEncoding:NSUTF8StringEncoding];
GLuint aPosition = glGetAttribLocation(program, aPositionCString);
First of all, we need to get a reference to the a_position variable in the vertex shader. This is done with glGetAttribLocation.
glVertexAttribPointer(aPosition, 2, GL_FLOAT, GL_FALSE, 0, square);
glEnableVertexAttribArray(aPosition);
glVertexAttribPointer defines the input and glEnableVertexAttribArray states that the data we’re inputting is dynamic for each vertex.
Draw
Now evertything is setup for us to draw. This is done with glDrawArrays.
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
The fourth argument defines how many vertices we want to draw. We’re setting it to four since, one for each vertex in the square.