views:

30

answers:

1

So I bought O'reilly's Iphone 3D programming and found what I believe to be a bug in there code. However I can't figure out what the problem is, and unless I do I can't move forward with my own code.

I will paste what I consider to be the appropriate code into this post but luckily all the code is available online at: http://examples.oreilly.com/9780596804831/HelloCone/

The problem I am having is with their OpenGL ES 2.0 renderer, it does not show up in their ES 1.1 renderer.

So what I have been noticing is that the cone does not render exactly in the correct position. To test this I changed the ModelViewMatrix to render exactly on the FrustumNear plane. So the cone should appear cut completely in two. When I do this with the ES 1.1 render this is the case, when I do the same in OpenGL ES 2.0 however it is not. The cone is for the most part there, but slightly shaved off. Meaning it is not landing exactly on the fustrum's near face.

Here is the initialization code where the projection matrix is created and set up:

void RenderingEngine2::Initialize(int width, int height)
{
const float coneRadius = 0.5f;
const float coneHeight = 1.0f;
const int coneSlices = 40;

{
    // Allocate space for the cone vertices.
    m_cone.resize((coneSlices + 1) * 2);

    // Initialize the vertices of the triangle strip.
    vector<Vertex>::iterator vertex = m_cone.begin();
    const float dtheta = TwoPi / coneSlices;
    for (float theta = 0; vertex != m_cone.end(); theta += dtheta) {

        // Grayscale gradient
        float brightness = abs(sin(theta));
        vec4 color(brightness, brightness, brightness, 1);

        // Apex vertex
        vertex->Position = vec3(0, 1, 0);
        vertex->Color = color;
        vertex++;

        // Rim vertex
        vertex->Position.x = coneRadius * cos(theta);
        vertex->Position.y = 1 - coneHeight;
        vertex->Position.z = coneRadius * sin(theta);
        vertex->Color = color;
        vertex++;
    }
}

{
    // Allocate space for the disk vertices.
    m_disk.resize(coneSlices + 2);

    // Initialize the center vertex of the triangle fan.
    vector<Vertex>::iterator vertex = m_disk.begin();
    vertex->Color = vec4(0.75, 0.75, 0.75, 1);
    vertex->Position.x = 0;
    vertex->Position.y = 1 - coneHeight;
    vertex->Position.z = 0;
    vertex++;

    // Initialize the rim vertices of the triangle fan.
    const float dtheta = TwoPi / coneSlices;
    for (float theta = 0; vertex != m_disk.end(); theta += dtheta) {
        vertex->Color = vec4(0.75, 0.75, 0.75, 1);
        vertex->Position.x = coneRadius * cos(theta);
        vertex->Position.y = 1 - coneHeight;
        vertex->Position.z = coneRadius * sin(theta);
        vertex++;
    }
}

// Create the depth buffer.
glGenRenderbuffers(1, &m_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER,
                      GL_DEPTH_COMPONENT16,
                      width,
                      height);

// Create the framebuffer object; attach the depth and color buffers.
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
                          GL_COLOR_ATTACHMENT0,
                          GL_RENDERBUFFER,
                          m_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
                          GL_DEPTH_ATTACHMENT,
                          GL_RENDERBUFFER,
                          m_depthRenderbuffer);

// Bind the color buffer for rendering.
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);

// Set up some GL state.
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);

// Build the GLSL program.
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());
}

And here is the Render code. As you can see I have changed the ModelVieMatrix to place the cone on the bottom left corner of the near Frustum face.

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(-1.6, -2.4, -5);

// 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);
}
+2  A: 

Looks like I found the answer to my own question.

The projection matrix in the O'Reilly code is being calculated incorrectly.

In their code they have:

T a = 2 * near / (right - left);
T b = 2 * near / (top - bottom);
T c = (right + left) / (right - left);
T d = (top + bottom) / (top - bottom);
T e = - (far + near) / (far - near);
T f = -2 * far * near / (far - near);
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 1;
return m;

However this is not the projection matrix. m.w.w should be 0 not 1.

Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 0;
return m;
Alex
Well done. Good to see that you didn't give up and kept looking for a solution. And thanks for a clear explaination of the problem with fix.
No one in particular
That's a great way to learn OpenGL ES. Be sure to let them know about the mistake so that they correct it for future printings.
Brad Larson
Yup I emailed the author yesterday. Hopefully he'll change the online code.
Alex