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:
(0, 0, 0)
and (1, 0, 0)
.camPosition = (-3, 0, 0)
and camLookAt = (0, 0, 0)
with up = (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
(0, 0, -3)
and the second one to (0, 0, -4)
.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
(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?
However, this isn't the case, (...) the (X,Y) components of the two transformed points do not match.
The two points having different x/y coordinates does not mean that they are not behind each other. Obviously, they have a different depth. The camera is located at the origin in this coordinate system. And if you calculate the direction from camera to the points (i.e. normalize(p - 0)
), we get:
direction1 = {0.707107, 0, -0.707107}
direction2 = {0.707107, 0, -0.707107}
So, the direction from the camera to the two points is the same, hence they are behind each other.
You haven't shown how you do the projection. But the points must project onto the same location. There is a little caveat with perspective projections, though. The projected point will have a w
-component that is not 1
. You need to divide the point by this w
-component (perspective divide) to find the actual projected point. And then, the x/y coordinates should actually match.