O'Reilly logo

iPhone 3D Programming by Philip Rideout

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

HelloCone with Shaders

Rather than modify the version of RenderingEngine2.cpp from HelloArrow, it will be more instructive if we can start our ES 2.0 backend by copying the contents of RenderingEngine1.cpp over whatever is already in RenderingEngine2.cpp, with two exceptions: you’ll need to save the BuildShader and BuildProgram methods from the existing RenderingEngine2.cpp from HelloArrow, so copy them somewhere safe for the moment. If you’re following along, do that now, and then you’ll be ready to make some changes to the file. Example 2-12 shows the top part of RenderingEngine2.cpp. New and changed lines are shown in bold. Some sections of unchanged code are shown as ..., so don’t copy this over the existing code in its entirety (just make the changes and additions shown in bold).

Example 2-12. RenderingEngine2 class declaration

#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "IRenderingEngine.hpp"
#include "Quaternion.hpp"
#include <vector>
#include <iostream>

#define STRINGIFY(A)  #A
#include "../Shaders/Simple.vert"
#include "../Shaders/Simple.frag"

static const float AnimationDuration = 0.25f;

...

class RenderingEngine2 : public IRenderingEngine {
public:
    RenderingEngine2();
    void Initialize(int width, int height);
    void Render() const;
    void UpdateAnimation(float timeStep);
    void OnRotate(DeviceOrientation newOrientation);
private:
    GLuint BuildShader(const char* source, GLenum shaderType) const;
    GLuint BuildProgram(const char* vShader, const char* fShader) const;
    vector<Vertex> m_cone;
    vector<Vertex> m_disk;
    Animation m_animation;
    GLuint m_simpleProgram;
    GLuint m_framebuffer;
    GLuint m_colorRenderbuffer;
    GLuint m_depthRenderbuffer;
};

The Initialize method almost stays as is, but this bit is no longer valid:

    glMatrixMode(GL_PROJECTION);
    glFrustumf(-1.6f, 1.6, -2.4, 2.4, 5, 10);
    
    glMatrixMode(GL_MODELVIEW);
    glTranslatef(0, 0, -7);

For ES 2.0, this changes to the following:

    m_simpleProgram = BuildProgram(SimpleVertexShader, 
                                   SimpleFragmentShader);
    glUseProgram(m_simpleProgram);

    // Set the projection matrix.
    GLint projectionUniform = glGetUniformLocation(m_simpleProgram, 
                                                   "Projection");
    mat4 projectionMatrix = mat4::Frustum(-1.6f, 1.6, -2.4, 2.4, 5, 10);
    glUniformMatrix4fv(projectionUniform, 1, 0, 
                       projectionMatrix.Pointer());

The BuildShader and BuildProgram methods are the same as they were for the ES 2.0 version of HelloArrow; no need to list them here. The shaders themselves are also the same as HelloArrow’s shaders; remember, the lighting is “baked,” so simply passing through the colors is sufficient.

We set up the model-view within the Render method, as shown in Example 2-13. Remember, glUniformMatrix4fv plays a role similar to the glLoadMatrix function in ES 1.1.

Example 2-13. RenderingEngine2::Render

void RenderingEngine2::Render() const
{
    GLuint positionSlot = glGetAttribLocation(m_simpleProgram, 
                                              "Position");
    GLuint colorSlot = glGetAttribLocation(m_simpleProgram, 
                                           "SourceColor");

    glClearColor(0.5f, 0.5f, 0.5f, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glEnableVertexAttribArray(positionSlot);
    glEnableVertexAttribArray(colorSlot);
    
    mat4 rotation(m_animation.Current.ToMatrix());
    mat4 translation = mat4::Translate(0, 0, -7);

    // Set the model-view matrix.
    GLint modelviewUniform = glGetUniformLocation(m_simpleProgram, 
                                                  "Modelview");
    mat4 modelviewMatrix = rotation * translation;
    glUniformMatrix4fv(modelviewUniform, 1, 0, modelviewMatrix.Pointer());
    
    // Draw the cone.
    {
      GLsizei stride = sizeof(Vertex);
      const GLvoid* pCoords = &m_cone[0].Position.x;
      const GLvoid* pColors = &m_cone[0].Color.x;
      glVertexAttribPointer(positionSlot, 3, GL_FLOAT, 
                            GL_FALSE, stride, pCoords);
      glVertexAttribPointer(colorSlot, 4, GL_FLOAT, 
                            GL_FALSE, stride, pColors);
      glDrawArrays(GL_TRIANGLE_STRIP, 0, m_cone.size());
    }
    
    // Draw the disk that caps off the base of the cone.
    {
      GLsizei stride = sizeof(Vertex);
      const GLvoid* pCoords = &m_disk[0].Position.x;
      const GLvoid* pColors = &m_disk[0].Color.x;
      glVertexAttribPointer(positionSlot, 3, GL_FLOAT, 
                            GL_FALSE, stride, pCoords);
      glVertexAttribPointer(colorSlot, 4, GL_FLOAT, 
                            GL_FALSE, stride, pColors);
      glDrawArrays(GL_TRIANGLE_FAN, 0, m_disk.size());
    }
    
    glDisableVertexAttribArray(positionSlot);
    glDisableVertexAttribArray(colorSlot);
}

The sequence of events in Example 2-13 is actually quite similar to the sequence in Example 2-11; only the details have changed.

Next, go through the file, and change any remaining occurrences of RenderingEngine1 to RenderingEngine2, including the factory method (and be sure to change the name of that method to CreateRenderer2). You also need to remove any occurrences of _OES and OES. Now, turn off the ForceES1 switch in GLView.mm; this completes the changes required for the shader-based version of HelloCone. It may seem silly to have added an ES 2.0 renderer without having added any cool shader effects, but it illustrates the differences between the two APIs.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required