Search code examples
openglsharpgl

glLookAt moves camera position when lookAt is moved - role of projection matrix?


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) 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
  • 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:

enter image description here

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?


Solution

  • 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.