Camera moves in wrong direction?

Here’s the vertex shader:

#version 120

uniform mat4 cammat;

attribute vec3 a_vert;
attribute vec2 a_tex;
attribute vec3 a_norm;

varying vec2 av_tex;

void main() {
    gl_Position = cammat * vec4(a_vert.xyz, 1);
    av_tex = a_tex;
}

Here’s the code that generates the matrix:

scen.activeCam = glm::mat4(1);
scen.activeCam *= glm::perspective(glm::radians(67.f), 640.0f / 480, 0.001f, 1000.f);
scen.activeCam = glm::ortho(-5, 5, -5, 5);
scen.activeCam = glm::translate(scen.activeCam, glm::vec3{0, 0, -2});
// somewhere else.
glUniformMatrix4fv(glGetUniformLocation(shaderprog, "cammat"), 1, GL_FALSE, glm::value_ptr(curscene->activeCam));

But if I translate activeCam by a positive X value, the camera seems to go left, when it should be going right (or all objects should be going left).
The mesh on the screen moves right, instead.

Which is to be expected. You aren’t moving a “camera” (OpenGL doesn’t have a camera), you’re moving the objects:

This transforms the positions of the vertices by the given matrix. If the matrix includes a translation in the positive X direction, the vertices will be moved in the positive X direction.

The viewpoint is always at 0,0,0 looking along the positive Z axis (conventional projection matrices as generated by e.g. glm::perspective or glm::ortho flip the Z direction, so it’s along the negative Z axis in eye space). To render the scene from a different viewpoint, you transform everything in the scene by the inverse transformation (e.g. to move the viewpoint to the right, you move everything to the left).

The easiest way to model a camera is to treat it like an object then pass the inverse of its transformation matrix to the vertex shader. Note that the projection shouldn’t be inverted, i.e. you want e.g. glm::perspective * glm::inverse(m) where m consists of the rotations and translations applied to the camera.

The fixed-function pipeline didn’t offer the ability to invert an arbitrary matrix, so the usual approach there was to invert the individual transformations (e.g. glTranslate(-x,-y,-z), glRotate(-angle,…), or glScale(1/x,1/y,1/z)) and apply them in the reverse order: for any matrices A,B,C, (A·B·C)-1 = C-1·B-1·A-1.

EDIT: Ah, didn’t read fully “the projection shouldn’t be inverted”.

So each mesh now has it’s own transform (also a mat4).

scen.activeCam = glm::perspective(glm::radians(67.f), 640.0f / 480, 0.001f, 1000.f);
scen.activeCam *= glm::lookAt(glm::vec3{0, 0, -2}, glm::vec3{0, 0, 0}, glm::vec3{0, 1, 0});

I’ve changed the eye argument’s Z to 2 to account for the inverse, and now the matrix uploading code looks like this:

glm::mat4 mat = glm::inverse(curscene->activeCam) * m->transform;
glUniformMatrix4fv(glGetUniformLocation(shaderprog, "cammat"), 1, GL_FALSE, glm::value_ptr(mat));

Now the mesh isn’t visible on screen.