Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Local Rotation problem

Started by Kasya Jun 30, 2010 at 1:23 PM 13 replies 1.4k views
Original Post
Kasya
Kasya
Hello,
I cannot get local rotation work correctly. Here is what i do:

Quaternion quat;Matrix matrix;float rot = 0.01f;void Rotate() { rot+= 0.01f; quat.FromAxisAngle(rot, Vector3(0, 1, 0)); quat.ToMatrix(matrix); mesh->matrix = matrix; //mesh->matrix is identity in the beginning}void Render() { Matrix final = modelviewMatrix * mesh->matrix; program->setUniformMatrix(uniforms["modelviewMatrix"], final.row); program->setUniformMatrix(uniforms["projectionMatrix"], projection.row);}


uniform mat4 modelviewMatrix;uniform mat4 projectionMatrix;attribute vec3 position;void main() { gl_Position = projectionMatrix * modelviewMatrix * vec4(position, 1.0);}


The result is rotation in a world space IMO.



instead of rotating with respect to rotation point in the left it rotates with respect to rotation point in the right. (The red dots)

Thanks,
Kasya
Bimble Bob
Bimble Bob
The name modelViewMatrix confuses me somewhat. What transformation is it describing?

In any case the order in which you multiply matrices together determines the order in which the transformations are applied. As such you want to make sure you apply the rotation before transforming the mesh into world space.

I think this would mean that, for the given example, you want to change

Matrix final = modelviewMatrix * mesh->matrix;

To
Matrix final = mesh->matrix * modelviewMatrix;


EDIT: It would seem that my "correction" is actually wrong. See below post for details on actual correct order of multiplication. Which means I don't know why you're not getting the result you want.

[Edited by - Dom_152 on June 30, 2010 3:00:58 PM]
It's not a bug... it's a feature!
Buckeye
Buckeye
The rotation matrix needs to be applied before you apply the translation.

The order of multiplication (in OGL):
projection * modelView * translation * local_rotation.

By applying the rotation to the modelView, the object is translated first, then rotated about the origin (as you've seen).

By applying the rotation before the translation, you'll rotate the object about the origin and then translate it to the "orbit" position.
Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly. You don't forget how to play when you grow old; you grow old when you forget how to play.
Bimble Bob
Bimble Bob
Then why doesn't:

Matrix final = modelviewMatrix * mesh->matrix;


(Where mesh->matrix is the rotation) yield the correct result?
It's not a bug... it's a feature!
Zakwayda
Zakwayda
Quote:
Original post by Dom_152
The name modelViewMatrix confuses me somewhat.
It's standard terminology in OpenGL; it simply refers to the transform that takes geometry from model (i.e. local) space to view (i.e. camera) space.

In the OpenGL fixed-function pipeline there's a dedicated 'modelview' matrix stack. With the programmable pipeline it's basically up to the user to construct whatever matrices are needed, but even in shader code it's still common to have named, combined transforms such as 'modelview' or 'modelviewprojection' (this is true in other shading languages, such as HLSL, as well).
Kasya
Kasya
Quote:
The order of multiplication (in OGL):
projection * modelView * translation * local_rotation.

By applying the rotation to the modelView, the object is translated first, then rotated about the origin (as you've seen).


I am not using any translation at all. It is just identity matrix. The only reason left for me is the entity initialization. I do like that:

entity = new MeshEntity(getMesh("CubeMesh"), Vector3(position));


the position is added to all vertices during ONLY initialization. but the absolute matrix is the same. I'll now check this out by checking position at (0,0,0)

EDIT: ^^^ Its rotating on world axis. Can this be a problem of gluLookAt? (i am using glhLookAtf2)
EDIT: Checked with rotation matrices to be sure that's not the quaternion problem. The same result.

Thanks,
Kasya

[Edited by - Kasya on July 1, 2010 5:54:26 AM]
haegarr
haegarr
When the positioning of the object is fixed into the vertices during loading, then you actually do
v := T * vorig
where T denotes the translation computed from the position, and v denotes the position corrected vertex. Then the transformation pipeline computes
v' := M * R * v
where R denotes the rotation and M all parts of the view and perhaps projection. Now, setting in the 1st into the 2nd shows us that
v' = M * R * ( T * vorig )
so there is a translation done before the rotation is applied, and hence the mesh will not rotate around its own local origin!

To overcome this, you can either not re-position the mesh's vertices (what is a legal but curious go anyway), or else undo the translation temporarily for the rotation. The undo would look like this:
v' = M * T * R * T-1 * ( T * vorig )
where you can see that T-1 * T cancels out (hence the term "undoing"). Comparing the formula with the one above, you can see that you have to operate with a modified rotation matrix
R' := T * R * T-1
to get the things work as expected.
Buckeye
Buckeye
Quote:
the position is added to all vertices during ONLY initialization

That's the problem. You're rotating an object about the origin that's not at the origin. Even if you "initialize" the translation at the start, you've translated it before you rotate it nonetheless.

Build the object at the origin and translate it after you've rotated it, or - translate it back to the origin, rotate it, translate back to position each frame.

By "building in" the translation as you've done, you have:

matrix = projection * modelView * rotation * (origin-based-model-vectors*translation)

where (I think) you want:

matrix = projection * modelView * translation * rotation * origin-based-model-vectors.

EDIT: oops. haegarr already said this mathematically.
Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly. You don't forget how to play when you grow old; you grow old when you forget how to play.
Kasya
Kasya
Thanks a lot)) I am currently building objects at the origin and it works. Just one question: why translation * rotation works?
haegarr
haegarr
Quote:
Original post by Kasya... Just one question: why translation * rotation works?
All points in space that are not affected by a general rotation together build the so-called axis of rotation. If you multiply the point [0,0,0] by a rotation matrix you'll see that [0,0,0] will be the result; ever. So [0,0,0] will ever by part of the axis of rotation, or in other words, each axis of rotation passes through [0,0,0].

If you havent translated the model yet, then its local origin is coincident with the global origin. Hence any rotation at this moment will use the global [0,0,0] in its axis which is at the same location as the local [0,0,0]. But if you have translated the object already, the local [0,0,0] will be apart from the global [0,0,0], and hence the model will no longer be rotated using the local origin.
Kasya
Kasya
I am really confused here. When i use rotation * translation, the object rotates in world space, when i use translation * rotation it rotates in local space. However when i use translation * rotation the object is not lighted after rotating 180 degree, but when i use rotation * translation it is lighted correctly.

return Matrix(	row[0] * m.row[0] + row[4] * m.row[1] + row[8] * m.row[2] + row[12] * m.row[3],	row[1] * m.row[0] + row[5] * m.row[1] + row[9] * m.row[2] + row[13] * m.row[3],	row[2] * m.row[0] + row[6] * m.row[1] + row[10] * m.row[2] + row[14] * m.row[3],	row[3] * m.row[0] + row[7] * m.row[1] + row[11] * m.row[2] + row[15] * m.row[3],	row[0] * m.row[4] + row[4] * m.row[5] + row[8] * m.row[6] + row[12] * m.row[7],	row[1] * m.row[4] + row[5] * m.row[5] + row[9] * m.row[6] + row[13] * m.row[7],	row[2] * m.row[4] + row[6] * m.row[5] + row[10] * m.row[6] + row[14] * m.row[7],	row[3] * m.row[4] + row[7] * m.row[5] + row[11] * m.row[6] + row[15] * m.row[7],	row[0] * m.row[8] + row[4] * m.row[9] + row[8] * m.row[10] + row[12] * m.row[11],	row[1] * m.row[8] + row[5] * m.row[9] + row[9] * m.row[10] + row[13] * m.row[11],	row[2] * m.row[8] + row[6] * m.row[9] + row[10] * m.row[10] + row[14] * m.row[11],	row[3] * m.row[8] + row[7] * m.row[9] + row[11] * m.row[10] + row[15] * m.row[11],	row[0] * m.row[12] + row[4] * m.row[13] + row[8] * m.row[14] + row[12] * m.row[15],	row[1] * m.row[12] + row[5] * m.row[13] + row[9] * m.row[14] + row[13] * m.row[15],	row[2] * m.row[12] + row[6] * m.row[13] + row[10] * m.row[14] + row[14] * m.row[15],	row[3] * m.row[12] + row[7] * m.row[13] + row[11] * m.row[14] + row[15] * m.row[15]	);


haegarr
haegarr
Quote:
Original post by Kasya
I am really confused here. When i use rotation * translation, the object rotates in world space, when i use translation * rotation it rotates in local space.
There are 2 possible views on that, the one operating in local space and the other in global space, but you must not mix them. I personally think of this issue as follows (maybe some other people will cry out):

For a composed transformation like
M3 * M2 * M1
it plays no role whether
( M3 * M2 ) * M1
or
M3 * ( M2 * M1 )
is used during computation (notice that the absolute order is every time the same).

When using the latter form and applying the particular transformations step by step (from right to left since we're speaking of column matrices, right?) each new transformation happens in a space that is exactly the same as the global space. I.e. before the first transformation is done, the model space is exactly orientated and located like the global space. So the first transformation can be seen to happen in the one or other because the result is the same.

After the 1st transformation the situation is altered: Now the model space is apart from the global space. However, the 2nd transformation is applied in a space that is still coincident with the global space. After its application also its local space is no longer coincident. But for the next (3rd) transformation it still is. ... So it can be seen so that each step from right to left happens in the global space.

That's it. But ...

You may say that Euler rotations consists of 3 consecutive rotations, e.g.
Rx * Ry * Rz
and is defined to happen in local space each. Then I answer that it is not a contradiction, due to the following:

If a transformation should happen in local space, then we only need to make that space coincident with the global space just in the moment when the transformation has to be applied. In other words, we first transform the current space so that is is coincident with the global space, apply the desired transformation, and then undo the first step:
M * R * M-1
Yes, this is exactly what I've shown some posts above!

In our case we have
Rx * Ry * Rz
The rightmost transformation Rz happens in a global coincident space anyway and hence need no handling. The next transformation Ry then happens in a space that is rotated by the former Rz. So we need to undo this before applying Ry and then to redo this:
Rx * ( Rz * Ry * Rz-1 ) * Rz
what is the same as
= Rx * Rz * Ry
Similarly for the 3rd transformation, but we must see that the local space is given by Rz * Ry, hence we need to do
( Rz * Ry ) * Rx * ( Rz * Ry )-1 * Rz * Ry
what is the same as
= Rz * Ry * Rx
isn't it? Now you can see the following: The order of transformations has changed from XYZ to ZYX! In other words: Although the rotations happen ever in global space, the reverse order lets look the transformations like they have happen in local space.

q.e.d.

In the example of a XYZ Euler rotation, so that local X then local Y then local Z should happen, the correct order would be
Rx * Ry * Rz although the order one would expect for column vectors normally (i.e. if the "local rotation" condition would not be given) would be the reverse one.

(Sorry if this looks a bit strange. But thinking in such formalisms helps at least me to solve such issues relatively easily.)
Kasya
Kasya
Ohhhh.. Get That. Thanks for help! I think i won't have any problems on that part AGAIN
haegarr
haegarr
Quote:
Original post by Kasya
Ohhhh.. Get That. Thanks for help! I think i won't have any problems on that part AGAIN
Then it was worth the effort :)
Kasya
Kasya
haha) yeah)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.