## [HowTo] Perspective Projection Matrix in OpenGL

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); glMatrixMode(GL_PROJECTION); glLoadMatrixf(m); // okay we can switch back to modelview mode // for all other matrices glMatrixMode(GL_MODELVIEW); ...

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

glUseProgram(shaderId); glUniformMatrix4fv("projMat", 1, GL_FALSE, m); RenderObject(); glUseProgram(0);

glMatrixMode(GL_PROJECTION);

glLoadMatrixf(m);

THese functions are both depricated in OGL 3.0 and up

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;

}

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 ?!

Regards

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 ðŸ˜‰

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:

http://www.songho.ca/opengl/gl_projectionmatrix.html

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?

Thanks,

Lars

@Lars

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.

-Sam

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

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?

@Mr. What: thanks for pointing out the mistake. Fixed!

whats the definition of PI_OVER_360`?

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.

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.

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.