Please help me spot what I'm missing in this code (a.k.a. newb rotating cubes again)

Hi all,

I’m trying to rotate a tutorial cube, just like probably everyone in the beginning, except without using the glm::lookAt(). This code works now, but I don’t get why… I probably just need a nudge in the right direction and someone to tell me that I’ve been missing something obvious.

The question is this: why does this code work (produce a cube which is NOT off screen) when translation (zoom) comes first, before the rotation?

Previous code used to have rotation first, and translation (zoom) at the end but the result looked like the cube rotated off the screen around the camera’s already translated position (obviously an order of multiplications problem).

Many, many thanks.

	
//5. MODEL-VIEW-PROJECTION TRANSFORMATIONS
	glm::mat4 m_MODEL;
	glm::mat4 m_VIEW;
	glm::mat4 m_PROJ;
	const glm::vec3 X(1.0f, 0.0f, 0.0f);
	const glm::vec3 Y(0.0f, 1.0f, 0.0f);
	const glm::vec3 Z(0.0f, 0.0f, 1.0f);

	//view (this is basically inverse of camera movement, applied to all meshes)?

	//zoom
	float dolly; //+ means content gets closer
	dolly = -3;
	m_VIEW = glm::translate(m_VIEW, glm::vec3(0, 0, dolly));

	//orbit
	float angle_x, angle_y; //+ means content rotates left/down
	angle_x = glm::radians(10.0f);
	angle_y = glm::radians(5.0f);
	m_VIEW = glm::rotate(m_VIEW, angle_x, X);
	//m_VIEW = glm::rotate(m_VIEW, angle_y, Y);

	//pan 
	float pan_x, pan_y; //pan content with respect to camera
	pan_x = 0;
	pan_y = 0;
	m_VIEW = glm::translate(m_VIEW, glm::vec3(pan_x, pan_y, 0));

You’re translating forward (in front of the viewer) by 3 units, then rotating about the X axis.

If you use that matrix as the model-view matrix, the resulting geometry will be moved 3 units in front of the viewer, and rotated about that point.

Remember: OpenGL doesn’t have a “camera”. The viewpoint is at the origin in eye space. To simulate a camera, you apply the inverse of the camera transformation to the geometry.

With the legacy OpenGL functions, the only way to do this was to rely upon the fact that (A.B)-1 = B-1.A-1. I.e. the camera transformation was decomposed into translation, rotation and (possibly) scale transformations, which were inverted and applied the reverse order. Individual transformations can be inverted by negating translations and rotation angles or by using the reciprocal of a scale factor.

With GLM, you can just construct the camera transformation just like an object transformation, then calculate its inverse.

The legacy matrix functions construct a relative transformation then multiply it by the current transformation, with the current transformation on the left and the relative transformation on the right. So a sequence of transformations are composed left-to-right. Rendering transforms geometry by the current transformation.

The effect is as if the sequence of transformations were applied to the geometry from right to left, i.e. in the reverse order:
(A.B.C).v = A.(B.(C.v)))

So rotation followed by translation will translate the geometry (so it is no longer at the origin), then rotate it about the origin.

By composing the sequence from left to right, the coordinate system in which subsequent transformations are interpreted changes as each one is applied. So a rotation rotates the coordinate system about the current origin, a subsequent translation translates by a given offset in the rotated coordinate system. If you translate then rotate, translation translates by a given offset in the initial coordinate system, rotation rotates about the origin of the translated coordinate system.

[QUOTE=GClements;1284689]The legacy matrix functions construct a relative transformation then multiply it by the current transformation, with the current transformation on the left and the relative transformation on the right. So a sequence of transformations are composed left-to-right. Rendering transforms geometry by the current transformation.

The effect is as if the sequence of transformations were applied to the geometry from right to left, i.e. in the reverse order:
(A.B.C).v = A.(B.(C.v)))[/QUOTE]

Thanks! I think that’s what threw me off (the order of matrix operands inside GLM transform functions). So to make sure I get it:

In order to construct, say, an Euler rotation of YX’Z’’ (or Y.(X.(Z.v))) in other words) as GLM matrix, I would have to :



const glm::vec3 X(1.0f, 0.0f, 0.0f);
const glm::vec3 Y(0.0f, 1.0f, 0.0f);
const glm::vec3 Z(0.0f, 0.0f, 1.0f);

glm::mat4 R; 
R = glm::rotate(R, angle_y, Y); 
R = glm::rotate(R, angle_x, X);
R = glm::rotate(R, angle_z, Z);


That’s how I understand this. I will of course test this.

[QUOTE=DailyFrankPeter;1284690]Thanks! I think that’s what threw me off (the order of matrix operands inside GLM transform functions). So to make sure I get it:

In order to construct, say, an Euler rotation of YX’Z’’ (or Y.(X.(Z.v))) in other words) as GLM matrix, I would have to : [/QUOTE]
GLM is largely designed to mimic the legacy OpenGL functions. So a function such as glm::rotate() takes the current transformation matrix (CTM) as an input and returns the updated CTM obtained by multiplying with the CTM on the left and the relative transformation on the right. So GLM calls go in the same order as the legacy OpenGL matrix functions, with the CTM being passed to each.

So:


glm::mat4 R;
R = glm::rotate(R, angle_y, Y); 
R = glm::rotate(R, angle_x, X);
R = glm::rotate(R, angle_z, Z);

is the same as


glm::mat4 I;
glm::mat4 Ry = glm::rotate(I, angle_y, Y); 
glm::mat4 Rx = glm::rotate(I, angle_x, X);
glm::mat4 Rz = glm::rotate(I, angle_z, Z);
glm::mat4 R = Ry * Rx * Rz;

I understand. Thanks again!

Just an afterthought - if it helps anyone to wrap their head around this, functions like glm::translate (judging by the documentation here: GLM_GTC_matrix_transform: Matrix transform functions.) are apparently not really meant for transforming an existing matrix but for ‘constructing a matrix representing respective transformation’ - which is why one has no choice over order of operands.

This choice is given in the form of multiplying thus obtained transformation matrices in any order you wish.

(I’m not sure where I got this idea from of functions letting you choose this in a one-liner - perhaps from Android, where methods like pretranslate(x,y) and posttranslate(x,y) exist. But I digress…)