To move the camera around I have 4 transforms:
- one to rotate the cam around x
- one to rotate the cam around y
- one to translate the cam in the x/y plane perpendicular to the camera
- one translation along the z axis to move the camera along that plane.
In code (in the vertex shader):
cam_to_world = rotatey(roty) * rotatex(rotx) * translate(vec3(tx, ty, 0)) * translate(vec3(0, 0, zoom));
The thing is that this doesn’t work: a camera moved 5 units away from the origin along the positive z-axis doesn’t see an object centred at the origin unless I apply a z-scale of -1 right at the start.
mat4 scale_z = mat4(1);
scale_z[2][2] = -1;
cam_to_world = scale_z * rotatey(roty) * rotatex(rotx) * translate(vec3(tx, ty, 0)) * translate(vec3(0, 0, zoom));
I don’t understand why?
The only explanation I could find is that by default, since opengl is right handed translating the camera 5 units along the positive z-axis for instance (lets ignore rotation and other translations in x or y) would move the camera away from the origin indeed, but the camera looks away from that origin. In other words to look at an object centred about the origin, we need to rotate the camera 180 degrees about the y-axis or scale its z axis by -1.
Could someone confirm or explain please.
Full code given below:
mat4 rotatex(float theta)
{
mat4 rot = mat4(1);
rot[0] = vec4(1, 0, 0, 0);
rot[1] = vec4(0, cos(theta), sin(theta), 0);
rot[2] = vec4(0, -sin(theta), cos(theta), 0);
return rot;
}
mat4 rotatey(float theta)
{
mat4 rot = mat4(1);
rot[0] = vec4(cos(theta), 0, -sin(theta), 0);
rot[1] = vec4(0, 1, 0, 0);
rot[2] = vec4(sin(theta), 0, cos(theta), 0);
return rot;
}
mat4 trans(vec3 tr)
{
mat4 rot = mat4(1);
rot[3] = vec4(tr, 1);
return rot;
}
mat4 persp()
{
#define M_PI 3.14159265
float fov = 70;
float near = 0.1;
float far = 100;
float image_aspect_ratio = 1.0;
float angle = tan(fov * 0.5 / 180.0 * M_PI);
float right = angle * near * image_aspect_ratio;
float left = -right;
float top = angle * near;
float bottom = -top;
mat4 persp = mat4(1);
persp[0] = vec4(2 * near / (right - left), 0, 0, 0);
persp[1] = vec4(0, 2 * near / (top - bottom), 0, 0);
persp[2] = vec4((left + right) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1);
persp[3] = vec4(0, 0, -2 * far * near / (far - near), 0);
return persp;
}
void main()
{
mat4 P; //<! the perspective matrix
mat4 V; //<! the camera-to-world matrix
mat4 M; //<! the object-to-world matrix
/*
// This order gives you Maya-like control
// rotate the camera around the y axis
// rotate the camera around the x axos
// move the camera z & y
// move away from the origin (zoom)
*/
mat4 scale_z = mat4(1);
scale_z[2][2] = -1;
V = scale_z * rotatey(roty) * rotatex(rotx) * trans(vec3(tx, ty, 0)) * trans(vec3(0, 0, zoom));
P = persp();
M = mat4(1); // use the idendity matrix in this example
vec3 eye = vec3(0, 0, 10);//vec3(V[3][0], V[3][1], V[3][2]);
vec3 s = normalize(eye - vp); // position of the eye - vertex pos
shade_col = vec3(max(0, vp[0]), max(0, vp[1]), max(0, vp[2]));//vec3(0.18 * abs(dot(s, vn)));
/*
// we need to take the inverse cam-to-world matrix because what we need here
// is to express points in world space into camera space. Thus we need to apply
// the world-to-cam matrix to the vertices in world space. After this transformation
// we can apply the perspective transformation matrix.
*/
gl_Position = P * inverse(V) * M * vec4(vp, 1); // our final obj-to-world * world-to-cam * persp vertex transform
}