# Is my perspective incorrect?

I am trying to implement a UVN free camera for browsing the 3D space of my OpenGL simulation and I noticed the perspective is introducing some sort of distortion. I observed it is more noticeable close to the border of the window and consequently far away from its center.

When inspecting the space employing an orthographic projection I notice no problems but when I switch to the perspective projection something odd happens. I have attached an image of the problem I am experiencing.

Each tile in the image is squared, with edges of length $$e$$, however, when rotating or simply translating the camera I can spot points of view from which tiles appear to be rectangular.

When I remove the plane and draw the three vectors $$(1, 0, 0)e'$$, $$(0, 1, 0)e'$$, and $$(0, 0, 1)e'$$ $$(e'=3*e)$$, I can spot perspectives from which the three do not appear to be orthogonal. I implemented quaternion-based rotation and the problem can be perceived when rotating. I also tried rotating the camera via the lookAt() method and a pair of angles but the problem is still there.

I spent a few days checking my calculations and I couldn't spot anything wrong. My matrices are computed on the CPU and are row major, when set as uniforms they are transposed.

This is my code for setting up the perspective matrix.

GLfloat
aspectRatio = GLfloat(width) / GLfloat(height);
Matrix4<GLfloat>::identity();
Matrix4<GLfloat>::elements[0][0] = +1.0f / (VFOV * aspectRatio);
Matrix4<GLfloat>::elements[1][1] = +1.0f / VFOV;
Matrix4<GLfloat>::elements[2][2] = (-Zfar - Znear) / (Znear - Zfar);

Matrix4<GLfloat>::elements[2][3] = +(2.0f * Zfar * Znear) / (Znear - Zfar);
Matrix4<GLfloat>::elements[3][2] = +1.0f;

Matrix4<GLfloat>::elements[3][3] = +GLfloat(0.0);


The image above has been captured with a FOV=30°.

Translation of the camera is achieved (inefficiently but simply) via the product with the translation matrix (identity with the fourth column having the column vector set to $$-position$$ of the camera).

I am not sure whether such distortion can be mitigated by the model, hence I don't know whether I am looking for a bug or I am fighting against a limitation of the camera model.

At this point, I have three questions:

1. Is the distortion I am perceiving due to the perspective?
2. If it is, is it to be expected (can not be prevented via such a simple model)?
3. Which model could account for it and correct it?

All help is appreciated.

• Perspective matrices do distort near the edges depending on the FOV, I suggest trying different values for the vertical FOV to see the different distortion amounts. Commented Aug 25 at 12:32
• @pmw1234 Thank you for the suggestion, I am giving it a shot. I just checked the presence of the vanishing point of my lines and there is exactly one so the distortion is really due to the FOV. Commented Aug 25 at 19:23

It's possible that your quaternion code and/or your use of the lookAt() method might have mistakes. However, the image that you included looks fine to me, and your perspective projection matrix checks out: When applied to a vector $$(x,y,z,1)^T$$ and followed by the perspective divide (which happens on the GPU automatically) it maps the view frustum onto the cube $$[-1,1]^3$$ in normalised device coordinates (NDCs). Moreover, the $$z$$ coordinate is mapped to an affine function of $$1/z$$, which means that by $$z$$-buffering, the GPU will solve the occlusion problem automatically, per fragment, by interpolating $$1/z$$ between the vertices of each triangle. There are settings in OpenGL and/or extensions to OpenGL, which choose between competing conventions for the bounds of that NDC cube (I think the difference is in whether $$z$$ is between $$-1$$ and $$1$$ or between $$0$$ and $$1$$, and I think that DirectX and OpenGL tend to differ on this, with the DirectX choice being arguably the more sensible one for some reason (floating-point precision and $$z$$-fighting ?) but I don't remember), so you might want to check that your own settings assume the $$[-1, 1]^3$$ convention.

This might sound silly and/or obvious, but, if you close one eye and position the other eye where the virtual camera is, relative to the graphics window on your monitor (meaning perpendicularly opposite the exact centre of your window, and at such a distance that the height of the window really does subtend an angle verticalFieldOfView at your eye) then the "perspective distorsion" should no longer seem like a distortion, because (apart from accommodation) this is pretty much how your eye/brain sees the world ! This is assuming that width and height really are in the same ratio as are the actual width and height of the window on your monitor. From this position, if you move your head either towards or away from the monitor, then the perspective will seem increasingly "distorted". I do this to check my own code.

EDIT: By "this is pretty much how your eye/brain sees the world" I mean that, if one were to look through a window from inside a building, with one eye closed, keeping the head perfectly still, and draw or paint on the glass of the window exactly what the open eye saw, as if tracing it, then the resulting image on the glass would coincide exactly with what your perspective matrix would produce, given an accurate 3d model of the outside world, with the view frustum positioned and oriented so as to represent the positions of your eye and of the pane of glass. It is in this sense that I claim the perspective projection is "not distorted".

Note that distinct vertical lines need not be parallel on screen after perspective projection, even though some engineering or artistic projections would have them parallel. Again, the perspective projection is the one that best models what one sees in real life - skyscrapers are the commonly given example of vertical lines seeming to converge.

EDIT: In the following image is my verification that your matrix is correct. In the small diagrams you can see how the image is projected through a point (your virtual eye) onto a plane (the monitor screen), which imitates the way that a pinhole camera works:

• Hello, thank you for the recap and for telling me the trick about closing one eye, I am new to this kind of thing. I eventually decided my implementation was ok, I reached this conclusion some days ago after comparing the image I posted with analogous perspectives rendered by professional software. I decided that if a professional rendering engine can accept these slight distortions, so can I. Commented Aug 29 at 19:56
• You are welcome, and I do apologise if I was somewhat telling you things you already knew. I am glad that you have resolved your issue ! I think that we still have a slight difference of opinion in that I believe your image, and those of the professional rendering engine, are not distorted. If you make a pinhole camera, as I used to do as a child, then you should find the images it produces align exactly with the images your code generates, given a geometrically correct model of the scene. If I am right, then it is good news for you ! But engineers, e.g., may prefer orthogonal projections. Commented Aug 29 at 23:05
• BTW I like to measure the graphics window on my monitor with a ruler, and also the distance between my face and the screen, and to set the parameters of my projection matrix accordingly. I have found that this produces images that are particularly convincing. One is used to looking at perspective projections made without obeying these strictures (e.g. looking at a photograph in a magazine from an arbitrary angle), and one is used to appreciating the perspective projection in spite of this. But I have found that getting it right produces a particularly compelling image. Next - depth of field ! Commented Aug 29 at 23:14
• PS I added a diagram to my answer, to show what I mean about the perspective projection modeling the behaviour of a pinhole camera. Commented Aug 29 at 23:40
• My engine has support for orthogonal projections and they provide convincing results as well. Now I think I get it, there is no distortion: I am just biased by binocular vision. Commented Aug 30 at 7:47