Part Six: Making Some Changes

Our first change to the OpenGL ES Application shell will be a very simple one. We will add another uniform variable to create movement on the X axis as well as the Y axis. We will also rename the existing uniform value to make it more descriptive of what it does. Since it only affects translations on the Y axis, it will be renamed from translate to translate_y, and our new uniform will be translate_x.

All of our changes will occur in the EDCubeDemoViewController.m view controller file and the Shader.vsh vertex shader file.

First, at the top of the EDCubeDemoViewController.m file, we’ll alter our uniform index enums to the following.

// Uniform index.
enum {
    //UNIFORM_TRANSLATE, // ED: Removed
    UNIFORM_TRANSLATE_Y, // ED: Added
    UNIFORM_TRANSLATE_X, // ED: Added
    NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];

Since we’re now dealing with a translate uniform variable for y and a different uniform variable for x, they should be more descriptive.

Now, in the drawFrame method, we’ll make a few changes.

- (void)drawFrame
{
    [(EAGLView *)self.view setFramebuffer];
   
    // Replace the implementation of this method to do your own custom drawing.
    static const GLfloat squareVertices[] = {
        -0.5f, -0.33f,
        0.5f, -0.33f,
        -0.5f,  0.33f,
        0.5f,  0.33f,
    };
   
    static const GLubyte squareColors[] = {
        255, 255,   0, 255,
        0,   255, 255, 255,
        0,     0,   0,   0,
        255,   0, 255, 255,
    };
   
    static float transY = 0.0f;
    static float transX = 0.0f; // ED: Added
   
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
   
    if ([context API] == kEAGLRenderingAPIOpenGLES2) {
        // Use shader program.
        glUseProgram(program);
       
        // Update uniform value.
        //glUniform1f(uniforms[UNIFORM_TRANSLATE], (GLfloat)transY); // ED: Removed
        glUniform1f(uniforms[UNIFORM_TRANSLATE_Y], (GLfloat)transY); // ED: Added
        transY += 0.075f;  
        glUniform1f(uniforms[UNIFORM_TRANSLATE_X], (GLfloat)transX); // ED: Added
        transX += 0.075; // ED: Added
       
        // Update attribute values.
        glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
        glEnableVertexAttribArray(ATTRIB_VERTEX);
        glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, squareColors);
        glEnableVertexAttribArray(ATTRIB_COLOR);
       
        // Validate program before drawing. This is a good check, but only really necessary in a debug build.
        // DEBUG macro must be defined in your debug configurations if that's not already the case.
#if defined(DEBUG)
        if (![self validateProgram:program]) {
            NSLog(@"Failed to validate program: %d", program);
            return;
        }
#endif
        /* ED: Removed
    } else {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
        transY += 0.075f;
       
        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);
   
    [(EAGLView *)self.view presentFramebuffer];
}

First, we add a new static float named transX to maintain our new x translation value.

Next, we update the call to glUniform1f() to use the updated TRANSLATE_Y uniform index enum, and add another glUniform1f() function call to use the new TRANSLATE_X uniform index enum.

We also add a line to increment the new transX static float variable by 0.075 radians.

Finally, I’m removing all of the OpenGL ES 1.1 code. We’re here to learn OpenGL ES 2.0, and there are a ton of OpenGL ES 1.1 tutorials out there, so that’s already covered.

Next, because we changed the name of a uniform variable and added a new one, we have to change the code in the loadShaders method that gets the uniform names from the program after linking it.

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;
   
    // Create shader program.
    program = glCreateProgram();
   
    // Create and compile vertex shader.
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname])
    {
        NSLog(@"Failed to compile vertex shader");
        return FALSE;
    }
   
    // Create and compile fragment shader.
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname])
    {
        NSLog(@"Failed to compile fragment shader");
        return FALSE;
    }
   
    // Attach vertex shader to program.
    glAttachShader(program, vertShader);
   
    // Attach fragment shader to program.
    glAttachShader(program, fragShader);
   
    // Bind attribute locations.
    // This needs to be done prior to linking.
    glBindAttribLocation(program, ATTRIB_VERTEX, "position");
    glBindAttribLocation(program, ATTRIB_COLOR, "color");
   
    // Link program.
    if (![self linkProgram:program])
    {
        NSLog(@"Failed to link program: %d", program);
       
        if (vertShader)
        {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader)
        {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (program)
        {
            glDeleteProgram(program);
            program = 0;
        }
       
        return FALSE;
    }
   
    // Get uniform locations.
    //uniforms[UNIFORM_TRANSLATE] = glGetUniformLocation(program, "translate"); // ED: Removed
    uniforms[UNIFORM_TRANSLATE_Y] = glGetUniformLocation(program, "translate_y"); // ED: Added
    uniforms[UNIFORM_TRANSLATE_X] = glGetUniformLocation(program, "translate_x"); // ED: Added
   
    // Release vertex and fragment shaders.
    if (vertShader)
        glDeleteShader(vertShader);
    if (fragShader)
        glDeleteShader(fragShader);
   
    return TRUE;
}

The only thing I changed here is near the bottom, where we no longer look for a uniform variable named ‘translate’, but instead get the uniform locations for ‘translate_y’ and ‘translate_x’.

And of course, none of this will work unless we update the vertex shader.

attribute vec4 position;
attribute vec4 color;

varying vec4 colorVarying;

//uniform float translate; // ED: Removed
uniform float translate_y; // ED: Added
uniform float translate_x; // ED: Added

void main()
{
    gl_Position = position;
    //gl_Position.y += sin(translate) / 2.0; // ED: Removed
    gl_Position.y += sin(translate_y) / 2.0; // ED: Added
    gl_Position.x += cos(translate_x) / 2.0; // ED: Added

    colorVarying = color;
}

The old ‘translate’ uniform was removed, and the renamed ‘translate_y’ and new ‘translate_x’ uniform variables were added.

I also had to update the logic to assign the y value to gl_Position because of the uniform name change, and I added a line to assign an x value to gl_Position as well.

Why did I use a cosine value instead of another sine value?

Remember that the sine value of an angle is the ratio of the side of a right triangle that is opposite of the angle to the hypotenuse?

 

 

The cosine value is the ratio of the side of a right angle that is adjacent to the angle to the hypotenuse, so as the sine value of an angle increases, the cosine value will decrease.

When we start out, the sine value of 0 is 0, but the cosine value of 0 is 1.

As we feed in angles, our position along the X axis will decrease as our position on the Y axis increases, resulting in a circular path around the origin, (0, 0).

Make the changes to your project code or download my version of the project after making these changes and run it. You’ll find our rainbow square moving in a nice big circle around the screen instead of just bouncing in place.

 

iPhone screen

 

What do you suppose would happen if you changed the shader code to make both the x and y values a sine value of the angle? What if they were both cosine values?

Don’t be afraid to play with the code, you’ll be surprised at what you might find.

Part Five | Index | Part Seven