Perspective Projection Matrix in OpenGL

HowTo - Programming


Here is a small function to create a perspective projection matrix. This function does not use OpenGL calls to initialize the matrix. This routine is nice because it can be used either with Direct3D (requires a little ogl to d3d matrix conversion – will be posted in another HowTo) or with the upcoming Larrabee too. And this function will be very useful to all coders that will need to produce OpenGL 3.1 compliant code because with OpenGL 3.1, you must have your own matrices functions!

void BuildPerspProjMat(float *m, float fov, float aspect, 
float znear, float zfar)
  float xymax = znear * tan(fov * PI_OVER_360);
  float ymin = -xymax;
  float xmin = -xymax;

  float width = xymax - xmin;
  float height = xymax - ymin;

  float depth = zfar - znear;
  float q = -(zfar + znear) / depth;
  float qn = -2 * (zfar * znear) / depth;

  float w = 2 * znear / width;
  w = w / aspect;
  float h = 2 * znear / height;

  m[0]  = w;
  m[1]  = 0;
  m[2]  = 0;
  m[3]  = 0;

  m[4]  = 0;
  m[5]  = h;
  m[6]  = 0;
  m[7]  = 0;

  m[8]  = 0;
  m[9]  = 0;
  m[10] = q;
  m[11] = -1;

  m[12] = 0;
  m[13] = 0;
  m[14] = qn;
  m[15] = 0;

and here is how to use it in an OpenGL 1 / OpenGL 2 code:

float m[16] = {0};
float fov=60.0f; // in degrees
float aspect=1.3333f;
float znear=1.0f;
float zfar=1000.0f;
BuildPerspProjMat(m, fov, aspect, znear, zfar);

// okay we can switch back to modelview mode
// for all other matrices

With a real OpenGL 3.0 code, we must use GLSL shaders and uniform variables to pass and exploit the transformation matrices:

glUniformMatrix4fv("projMat", 1, GL_FALSE, m);


13 thoughts on “Perspective Projection Matrix in OpenGL”

  1. luuk


    THese functions are both depricated in OGL 3.0 and up

  2. Zenja

    You can cut down the number of calculations (and eliminate the multiplactions by -1) but simplifying the terms:
    const float h = 1.0f/tan(fov*PI_OVER_360);
    float neg_depth = zNear-zFar;

    m[0] = h / aspect;
    m[1] = 0;
    m[2] = 0;
    m[3] = 0;

    m[4] = 0;
    m[5] = h;
    m[6] = 0;
    m[7] = 0;

    m[8] = 0;
    m[9] = 0;
    m[10] = (zFar + zNear)/neg_depth;
    m[11] = -1;

    m[12] = 0;
    m[13] = 0;
    m[14] = 2.0f*(zNear*zFar)/neg_depth;
    m[15] = 0;

  3. Damien

    The “how to use it” code is confusing; the GL Matrix functions are supposed to be removed in ogl 3.1. Maybe i’m wrong but i think that you must provide the matrix to the shaders using parameters ?!


  4. JeGX Post Author

    Thanks guys for your feedbacks!

    Luuk and Damien > you’re both right. In a proper GL 3.1 code, we must use a glsl shader to render an object and tranformation matrices must be passed with uniforms. But I wanted to give a simple GL 2 code. I update a bit the post…

    Zenja > thanks for the optimization 😉

  5. Lars

    Thanks for the nice code example.

    I have used matrices, in school, in the distant past, but have only
    recently started using OpenGL, OpenCV, etc.

    Over the past few days I have spent a lot of time trying to
    learn about matrices, perspective projection, homogeneous
    coordinates, and related topics.

    I see that the matrix presented in your code is similar to that
    for the perspective projection presented at:
    except that your example has it this way:

    m[8] = 0;
    m[9] = 0;
    m[10] = q;
    m[11] = -1;

    m[12] = 0;
    m[13] = 0;
    m[14] = qn;
    m[15] = 0;

    and the other web site does it like this:

    m[8] = 0;
    m[9] = 0;
    m[10] = q;
    m[11] = qn;

    m[12] = 0;
    m[13] = 0;
    m[14] = -1;
    m[15] = 0;

    Why is that?
    I think that the answer probably lies in the use of
    homogeneous coordinates but I don’t understand it yet.
    Can you help me with this?


  6. Sam


    It looks like the two matrices are the same, just transposed. The third parameter of glUniformMatrix4fv can transpose for you. Assuming you get projMat from the code on this page you can either transpose it like this:

    /* c code */
    glUniformMatrix4fv(“projMat”, 1, GL_TRUE, m);

    /* shader code */
    gl_Position = projMat * vertex_position;

    OR you can choose not to transpose and do your multiplication bass ackwards in the shader.

    /* c code */
    glUniformMatrix4fv(“projMat”, 1, GL_FALSE, m);

    /* shader code */
    gl_Position = vertex_position * projMat;

    OR you could rewrite the projection like in the other website. That way you don’t have to transpose for your shader, you can multiply in mat * vec order, and your matrix is laid out in memory in row major order as God intended.

    IMHO the other website uses the superior convention.


  7. Sam

    Oops. The first argument to glUniformMatrix4fv() should be a GLint location, not an actual name.

  8. Mr. What

    In this example, xmax, ymax are not defined, as used in the 3rd and 4th lines of the function. should they be replaced with xymax?

  9. Neil Kolban

    In the code, when I look at the 16 element float array, I can’t tell if the matrix is defined row major or column major. It would be wonderful to see a description of the matrix as a 4 x 4 grid.

  10. Andrew

    What is the definition of PI_OVER_360?
    I’m guessing that is for converting from degrees to radians, and it is meant to represent (2PI / 360).

    2pi because pi represents 180 degrees.

    Though in another example that same section is written as
    (fieldOfView / 360.0f * 3.14159f)
    Which suggests it is actually meant to be (PI/360)
    Very confusing. I do not get why it would be this.
    Would be good if someone could share.

  11. Gordon

    PI / 360 = PI / 180 * 0.5

    So really, its a my concise way of saying:

    tan(FOV * 0.5 * PI/180)

    So, really what we are doing is taking half the angle of the FOV, converting to radians, and then calculating the tangent.

Comments are closed.