Short version: I'm using a glLookAt view matrix with a perspective projection matrix, and I'm wondering why a change in the camLookAt vector (the point where I'm looking at) leads to a change in the camera's position.
Long version: I'm using the code described here for my own implementation of the well-known glLookAt function:
var z = glm.normalize(camPosition - camLookAt);
var y = up;
var x = glm.normalize(glm.cross(y, z));
y = glm.cross(z, x);
var result = new mat4(1.0f);
result[0, 0] = x.x;
result[1, 0] = x.y;
result[2, 0] = x.z;
result[3, 0] = -Dot(x, camPosition);
result[0, 1] = y.x;
result[1, 1] = y.y;
result[2, 1] = y.z;
result[3, 1] = -Dot(y, camPosition);
result[0, 2] = z.x;
result[1, 2] = z.y;
result[2, 2] = z.z;
result[3, 2] = -Dot(z, camPosition);
result[0, 3] = 0;
result[1, 3] = 0;
result[2, 3] = 0;
result[3, 3] = 1.0f;
return result;
This seems to work nicely, except for one strange thing: When I leave the cam's position fixed, but change the camLookAt
variable, it seems that the camera moves not just its angle, but also its position.
To demonstrate it, I have made an explicit calculation:
- Take the world points
(0, 0, 0)
and(1, 0, 0)
. - Look at them from
camPosition = (-3, 0, 0)
andcamLookAt = (0, 0, 0)
withup = (0, 0, 1)
. So we're looking at the origin, in the direction of the positive x axis. This is the matrix I get:
0, -1, 0, 0 0, 0, 1, 0 -1, 0, 0, -3 0, 0, 0, 1
- Since both points lie on the x axis, they should appear at the same spot on our camera. One dot should be exactly behind the other one. This works great, the first point gets mapped to
(0, 0, -3)
and the second one to(0, 0, -4)
. - Now change
camLookAt
to(0, 3, 0)
, i.e. rotate the camera by 45 degrees to the left. This is the matrix I get:
0.71, -0.71, 0.00, 2.12 0.00, 0.00, 1.00, 0.00 -0.71, -0.71, 0.00, -2.12 0.00, 0.00, 0.00, 1.00
- Since we don't move the camera's position, the points should still be behind each other. However, this isn't the case, the two points get transformed to
(2.1213, 0, -2.1213)
and(2.8284, 0, -2.8284)
respectively. So the (X,Y) components of the two transformed points do not match.
Now, up to this point we have used Euclidean transformations only, so there is no perspective yet. However, I'm using a perspective projection matrix like this
const float radians = (60.0f / 360.0f) * (float)Math.PI * 2.0f;
projectionMatrix = glm.perspective(radians, width / height, 0.01f, 1000f);
Even after this projection matrix is applied, the two points don't land on the same (X, Y) coordinates, i.e. on the same pixel on screen.
What am I missing? Is that the intended behavior of glLookAt, or am I using / implementing it wrong?
EDIT: The projection matrix is applied in the vertex shader, simply like this:
void main(void) {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0);
}
EIDT 2: Here's a recording of my current scene, just as clarification. The only thing that's being changed with time is the camLookAt
vector. The camera should stay at the same position, but somehow it seems to move:
EDIT 3: As recommended by @NicoSchertler, I just did all the calculations on the CPU. The viewMatrix multiplication is done above, and it results in (2.1213, 0, -2.1213)
and (2.8284, 0, -2.8284)
respectively. Now I apply the projection matrix (see above for how I'm getting the projection matrix):
1.30, .00, .00, .00 .00, 1.73, .00, .00 .00, .00, -1.00, -.02 .00, .00, -1.00, 1.00
and I get the following vectors
(2.75, 0.00, 2.10, 3.12)
(3.67, 0.00, 2.81, 3.83)
Then I divide them by their w
value which yields
(0.88, 0.00, 0.67, 1.00)
(0.96, 0.00, 0.73, 1.00)
So they clearly are not behind each other.
Is there a bug in the projection matrix?