# What is the proper way to create a camera matrix?

I am having issues when trying to create a camera transform. It seems to have odd distortions and it does not seem to rotate properly, though it will translate alright. While I have seen issues like this where the rotation does not work, I have not found any like my issue.

This is what I currently do to create the matrix:

Mat4 TransformLookAt(Vec3 from, Vec3 to, Vec3 world_up) {
Mat4 view_matrix = Mat4New(1.0f);
Mat4 rot_matrix = Mat4New(1.0f);

Vec3 axis_n = Vec3Normalise(Vec3Subtract(to, from));
Vec3 axis_v = Vec3Normalise(Vec3Cross(from, Vec3Cross(world_up, from)));
Vec3 axis_u = Vec3Normalise(Vec3Cross(axis_n, axis_v));

// Rotate on the X-axis
rot_matrix.ax = axis_u.x;
rot_matrix.ay = axis_u.y;
rot_matrix.az = axis_u.z;

// Rotate on the Y-axis
rot_matrix.bx = axis_v.x;
rot_matrix.by = axis_v.y;
rot_matrix.bz = axis_v.z;

// Rotate on the Z-axis
rot_matrix.cx = axis_n.x;
rot_matrix.cy = axis_n.y;
rot_matrix.cz = axis_n.z;

// Translate the camera
view_matrix.aw = -from.x;
view_matrix.bw = -from.y;
view_matrix.cw = -from.z;

view_matrix = Mat4ByMat4Multiply(view_matrix, rot_matrix);

return view_matrix;
}


The camera is at (1.0, 0.0, 3.0) at is set to look at (0.0, 0.0, 0.0), which is where the centre of the cube is located. Here is what it looks like:

I would expect to see the left side and possibly top of the cube (since that is what this is supposed to do), but instead the cube has been distorted so that its left side cannot be seen.

That leads me to this question: If none of the methods like the one I've tried to implement works, then what is the proper way to create a camera matrix? What am I missing so that this does not work?

I understand how it should work, but I just can't get it to work. I feel like it might be something obvious, but I can't seem to figure it out.

• // Rotate on the X-axis these comments are really misleading, what you are doing is setting what the 'local' versions of these axes are in 'global' coordinates Vec3 axis_v = Vec3Normalise(Vec3Cross(from, Vec3Cross(world_up, from))); this is confusing too - shouldn't 'from' be 'axis_n'? Apr 5, 2021 at 1:00

I recommend that you try GLM for your math library. Here is some sample code from my camera class -- https://github.com/sjhalayka/opengl4_stlview/blob/6ea4b942a8a5b40f923f5cd99da0cdec5e06e0e4/uv_camera.cpp#L48

Until you switch to GLM, here is another code for your reference:

// https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
// https://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml

void multiply_4x4_matrices(float (&in_a)[16], float (&in_b)[16], float (&out)[16])
{
//   matrix layout:
//
//   [0 4  8 12]
//   [1 5  9 13]
//   [2 6 10 14]
//   [3 7 11 15]

out[0] = in_a[0] * in_b[0] + in_a[4] * in_b[1] + in_a[8] * in_b[2] + in_a[12] * in_b[3];
out[1] = in_a[1] * in_b[0] + in_a[5] * in_b[1] + in_a[9] * in_b[2] + in_a[13] * in_b[3];
out[2] = in_a[2] * in_b[0] + in_a[6] * in_b[1] + in_a[10] * in_b[2] + in_a[14] * in_b[3];
out[3] = in_a[3] * in_b[0] + in_a[7] * in_b[1] + in_a[11] * in_b[2] + in_a[15] * in_b[3];
out[4] = in_a[0] * in_b[4] + in_a[4] * in_b[5] + in_a[8] * in_b[6] + in_a[12] * in_b[7];
out[5] = in_a[1] * in_b[4] + in_a[5] * in_b[5] + in_a[9] * in_b[6] + in_a[13] * in_b[7];
out[6] = in_a[2] * in_b[4] + in_a[6] * in_b[5] + in_a[10] * in_b[6] + in_a[14] * in_b[7];
out[7] = in_a[3] * in_b[4] + in_a[7] * in_b[5] + in_a[11] * in_b[6] + in_a[15] * in_b[7];
out[8] = in_a[0] * in_b[8] + in_a[4] * in_b[9] + in_a[8] * in_b[10] + in_a[12] * in_b[11];
out[9] = in_a[1] * in_b[8] + in_a[5] * in_b[9] + in_a[9] * in_b[10] + in_a[13] * in_b[11];
out[10] = in_a[2] * in_b[8] + in_a[6] * in_b[9] + in_a[10] * in_b[10] + in_a[14] * in_b[11];
out[11] = in_a[3] * in_b[8] + in_a[7] * in_b[9] + in_a[11] * in_b[10] + in_a[15] * in_b[11];
out[12] = in_a[0] * in_b[12] + in_a[4] * in_b[13] + in_a[8] * in_b[14] + in_a[12] * in_b[15];
out[13] = in_a[1] * in_b[12] + in_a[5] * in_b[13] + in_a[9] * in_b[14] + in_a[13] * in_b[15];
out[14] = in_a[2] * in_b[12] + in_a[6] * in_b[13] + in_a[10] * in_b[14] + in_a[14] * in_b[15];
out[15] = in_a[3] * in_b[12] + in_a[7] * in_b[13] + in_a[11] * in_b[14] + in_a[15] * in_b[15];
}

// centre is the look at position
void init_perspective_camera(float fovy, float aspect, float znear, float zfar,
float eyex, float eyey, float eyez, float centrex, float centrey,
float centrez, float upx, float upy, float upz,
float (&model_matrix)[16],
float (&view_matrix)[16],
float (&projection_matrix)[16])
{
get_model_matrix(model_matrix);
get_view_matrix(eyex, eyey, eyez, centrex, centrey, centrez, upx, upy, upz, view_matrix);
get_projection_matrix(fovy, aspect, znear, zfar, projection_matrix);
}

void get_model_matrix(float (&in_a)[16])
{
// Identity matrix
in_a[0] = 1; in_a[4] = 0; in_a[8] =  0; in_a[12] = 0;
in_a[1] = 0; in_a[5] = 1; in_a[9] =  0; in_a[13] = 0;
in_a[2] = 0; in_a[6] = 0; in_a[10] = 1; in_a[14] = 0;
in_a[3] = 0; in_a[7] = 0; in_a[11] = 0; in_a[15] = 1;
}

void get_view_matrix(float eyex, float eyey, float eyez, float centrex, float centrey, float centrez, float upx, float upy, float upz, float (&in_a)[16])
{
vertex_3 f, up, s, u;

f.x = centrex - eyex;
f.y = centrey - eyey;
f.z = centrez - eyez;
f.normalize();

up.x = upx;
up.y = upy;
up.z = upz;
up.normalize();

s = f.cross(up);
s.normalize();

u = s.cross(f);
u.normalize();

in_a[0] = s.x;  in_a[4] = s.y;  in_a[8] = s.z;   in_a[12] = 0;
in_a[1] = u.x;  in_a[5] = u.y;  in_a[9] = u.z;   in_a[13] = 0;
in_a[2] = -f.x; in_a[6] = -f.y; in_a[10] = -f.z; in_a[14] = 0;
in_a[3] = 0;    in_a[7] = 0;    in_a[11] = 0;    in_a[15] = 1;

float translate[16];
translate[0] = 1; translate[4] = 0; translate[8] = 0;  translate[12] = -eyex;
translate[1] = 0; translate[5] = 1; translate[9] = 0;  translate[13] = -eyey;
translate[2] = 0; translate[6] = 0; translate[10] = 1; translate[14] = -eyez;
translate[3] = 0; translate[7] = 0; translate[11] = 0; translate[15] = 1;

float temp[16];
multiply_4x4_matrices(in_a, translate, temp);

for(size_t i = 0; i < 16; i++)
in_a[i] = temp[i];
}

void get_projection_matrix(float fovy_degrees, float aspect, float znear, float zfar, float (&in_a)[16])
{
const static float pi = 4.0f*atanf(1.0);

// Convert fovy to radians, then divide by 2
float f = 1.0f / tan(fovy_degrees/360.0f*pi);

in_a[0] = f/aspect; in_a[4] = 0; in_a[8] = 0;                              in_a[12] = 0;
in_a[1] = 0;        in_a[5] = f; in_a[9] = 0;                              in_a[13] = 0;
in_a[2] = 0;        in_a[6] = 0; in_a[10] = (zfar + znear)/(znear - zfar); in_a[14] = (2.0f*zfar*znear)/(znear - zfar);
in_a[3] = 0;        in_a[7] = 0; in_a[11] = -1;                            in_a[15] = 0;
}