# how to concatenate 3d rotations?

This topic is 1217 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

i have a ~bootstrap background in 1d audio dsp which lends me an idiosyncratic manner when coding. i decided to write my own 3d soft engine with geometric knowledge and ran into what seems to be the canonical issue..

i do understand everyone wants to jump ahead to telling me the solution, but let's state the problem for explicitness -

the problem:
in the illustration, the observer turns left, then rolls to the left, positioning the object that was initially straight ahead to the lower right of the frustrum. and of course, subsequent commands to turn left or right are desired to be applied from the current orientation, whereas in the illustration, turning left or right is applied to the now disjunct world y axis (which would only be a correct application for left/right turns for an upright observer).

so the problem is applying a rotation in terms of a given arbitrary orientation.. i've seen dozens of threads wording it in various ways.. rotating on an arbitrary pole, applying consecutive rotations.. a ton of language has been applied to describing this issue.

you have probably intuited that i am comfortable with 2d geometry and am aspiring for a yaw-pitch-roll implementation. i *really* would like to store my 3d rotations as 3 radian values.

the solution:
everybody can tell me that the solution for this is to adopt quaternions. or, maybe matrices, some will say. i've even seen a few cryptic sources with long reams or arctan functions that didn't use matrices.

i have been researching for a couple of weeks, and there are a few reasons why i haven't been able to commit myself to quaternions, the most meaningful is that i have yet to determine if i am actually going to be able to solve the problem i am having by using them...

what i would like to do is concatenate radian rotations and return a radian rotation.......

.......start with three radian values for my current orientation, a.x, a.y, and a.z.. and then take my second radian rotation vector, b.x, b.y, b.z, and put these together so i have a radian rotation vector that is the new state of orientation.

as i look at matrices and quaternions, what i see is a mechanism for applying rotation.. turn my radian data (tait-bryan angles, or incorrectly "euler angles") into a matrix i can use to calculate the new location of objects.. multiply two for successive rotations.. ..but i don't see anything about giving me back radians, which is what i want.

i've been reading.. a whole mess of stuff... sten melax's pdf is fair, the colorado school of mines rotation pdf is also fair.. but for all the information there is, i'm not able to apply it to the scope of the task i'd like to address...

eg. i only recently understood (col. mines pdf) that 3x3 matrices can be used to apply successive rotations with one transformation "just as well as quaternions" ?? which would be nice because having happily rudimentary aesthetics, i eschew matrices as bulk and therefore struggle every time something is presented in terms of such concepts... if someone says "multiply these two matrices to get the result," i know i can do it, but i know i'm going to be using reference material when implementing it.

i have sort of a trauma complex from reading too much lol. instead of continuing to dig through things hoping to someday get somewhere i can start doing things, i thought i'd simply ask: what's the fastest way to get my simple brain into returning compounded rotation as radians?

Edited by xoxos

##### Share on other sites

thank you.. i'd about realised it and you've said it exactly.

##### Share on other sites

Convert the rotations to either matrix or quaternion form, multiply them and convert back.

##### Share on other sites

Convert the rotations to either matrix or quaternion form, multiply them and convert back.

That only works in 2D. In 3D, rotations are quite a bit more complicated.

##### Share on other sites

i know this question may seem banally dysfunctional, but this is the part that is challenging me :)

i reference matrix multiplication as:

given two matrices -
012    abc
345    def
678    ghi

0*a+1*d+2*g,  0*b+1*e+2*h,  0*c+1*f+2*i
3*a+4*d+5*g,  3*b+4*e+5*h,  3*c+4*f+5*i
6*a+7*d+8*g,  6*b+7*e+8*h,  6*c+7*f+8*i

..but references for flight systems present this:
ch*ca,     -ch*sa*cb + sh*sb,     ch*sa*sb + sh*cb
sa,     ca*cb,             -ca*sb
-sh*ca,     sh*sa*cb + ch*sb,     -sh*sa*sb + ch*cb

if one assumes
sa = sin(attitude)
ca = cos(attitude)
sb = sin(bank)
cb = cos(bank)

i admit my cretinous nature from not taking the time to calculate multiplying the separate axis rotations together to verify that result and see if it does corroborate the referenced method,

..but i would expect a much lengthier solution from that method (i have yet to find a reference on 3d rotations that doesn't say "matrix multiplication is documented everywhere, so we can skip it").

is there a special case for performing matrix multiplication in this circumstance, or is my reference correct? is there an award for especially lazy posters?

##### Share on other sites
i admit my cretinous nature from not taking the time to calculate multiplying the separate axis rotations together to verify that result and see if it does corroborate the referenced method,

..but i would expect a much lengthier solution from that method (i have yet to find a reference on 3d rotations that doesn't say "matrix multiplication is documented everywhere, so we can skip it").

is there a special case for performing matrix multiplication in this circumstance, or is my reference correct? is there an award for especially lazy posters?

Your generic matrix multiplication is correct, and so is probably the final result of the A/B/H matrix you showed (depends on how the rotations are combined, but it does look reasonable). The individual attitude, bank and heading matrices are axis-aligned rotations and thus contains plenty of zero-valued elements. An axis aligned rotation matrix has 5 zero-valued elements and 4 non-zero valued elements. If you insert that many zero-valued elements into your matrix multiplication, many of the individual products disappear. The product of the three axis aligned matrices isn't any more complex than that when all zero-valued terms are eliminated from the equation.

##### Share on other sites

ty, it's nice to simply know i have the correct method referenced! i can now apply myself with energy reserved for intentful achievement.. :)

##### Share on other sites

is there an award for especially lazy posters?

If there is, sign me up. :)

##### Share on other sites

lol okay, this is making me wonder why i'm alive so let's do this piece by piece.. just the basics first, if this is all straight i can address rotation order..

my game loop worked fine with the non-concatenating rotations as illustrated in first post, it ought to be solid so that no data is being accidentally overwritten -

// game loop
tetra shape1 = shape0; // create real instance of ideal, predefined tetrahedron form
shape1.location += (0,0,5); // move real object to a fixed location
shape1.rotate(place0.r); // rotate by player-world orientation

..so i'm not overwriting any location or rotation data that could cause unanticipated results. that would have evinced itself with my naive rotation, which worked perfectly.

to perform my clumsy, first matrix rotation implementation i created a "matrix3x3" class. matrix data is stored in variables "a" through "i", and clumsy functions are used to populate the variables. at least, this is the way to do it before grace sets in and i have a better idea of where and how to write it.

my matrix multiplication looks alright to me!

        matrix3x3& operator * (matrix3x3 param) {
float temp0, temp1, temp3, temp4, temp6, temp7;
temp0 = a * param.a + b * param.d + c * param.g;
temp1 = a * param.b + b * param.e + c * param.h;
c = a * param.c + b * param.f + c * param.i;
temp3 = d * param.a + e * param.d + f * param.g;
temp4 = d * param.b + e * param.e + f * param.h;
f = d * param.c + e * param.f + f * param.i;
temp6 = g * param.a + h * param.d + i * param.g;
temp7 = g * param.b + h * param.e + i * param.h;
i = g * param.c + h * param.f + i * param.i;
a = temp0;    b = temp1;
d = temp3;    e = temp4;
g = temp6;    h = temp7;
return *this;
}


..after the previously noted game loop, my rotation vector place0.r is updated by my rotating vector, place0.w -

        matrix3x3 tempmat0, tempmat1;
tempmat0 = tempmat0.make(place0.r);
tempmat1 = tempmat1.make(place0.w);
tempmat0 = tempmat0 * tempmat1;
place0.r = tempmat0.angles(); // convert matrix to angle vector


i know that's all basic and ugly to look at, but let's confirm it's correct before the travesty of rotations begins. i've been using POVray for almost two decades and so i want to go in the "wrong" direction and make a left handed implementation. i've rewritten it four times now, finally adopting the right handed system from euclideanspace.com and i'm still getting useless results with that.. so, first.. the matrix is correct.. then on to order of rotations and the matrix coefficients...

..one of the things that is making me seriously wonder about my method is that place0.r is a set of three angles from 0 to 2*pi.. place0.w is another set of three angles created by player input.. eg. turn left or right, place0.w.y is modified.. ..when the player is not holding a button to move left, place0.w (all three values..) glide back to 0, so that rotation will slow instead of abruptly stop. however,

(all must be forgiven because trying to fenangle the matrix-to-euler conversion for an arbitrary rotation system is not fun)

however, what i found is that my place0.r value was being modulated back to (0,0,0) in the same glide time applied to releasing the place0.w.. i'm having to hold the key down and fight to make it *stay* facing anywhere (and the rotations all have ganky, curved paths.. which we all know means a bug is somewhere). i have no idea why it's doing that but i don't think it should be :)

so, next, rotation order..

##### Share on other sites

Why does operator * modify the left-hand-side operand? Maybe you are thinking of operator *=?

Can you give us an example of a particular pair of rotations, the rotation you expected to get when composing them and the rotation you ended up getting instead?

##### Share on other sites

you may have found the cause there.. my "extensive experience in coding" has been without pointers and classes, i'd jury rigged the syntax from a tutorial on 2d vectors and thought the & potentiated using *this to return the function.. lolz. i'll have to brush up and go through my classes.

i'll post two rotations, the LH/LH which is incomplete because i have no idea which coefficients to apply to the matrix-to-euler, hoping to pick it out of comparing my calculated LH/LH matrix with the referenced RH/RH matrix..

left handed/left handed ought to look like
1   0   0

0   cx -sx

0  sx  cx

cy  0   sy

0   1   0

-sy 0  cy

cz -sz 0

sz cz  0

0  0   1

my calculation for an x,y,z rotation order-

cy*cz                         cy*-sz                        sy

-sx*-sy*cz + cx*sz      -sx*-sy*-sz + cx*cz     -sx*cy
cx*-sy*cz + sx*sz       cx*-sy*-sz + sx*cz      cx*cy

the RH/RH system i implemented is from
http://www.euclideanspace.com/maths/geometry/rotations/euler/

ch*ca                     sh*ca                     -sa

ch*sa*sb - sh*cb    sh*sa*sb + ch*cb    ca*sb

ch*sa*cb + sh*sb   sh*sa*cb - ch*sb     ca*cb

using this system, panning left or right moves the object in an S path, the horizontal rising to the right and dipping to the left.. as if panning off center rolls the camera increasingly clockwise. my attempts with the LH/LH system resulted in aliasing with any deviation from heading(0,0,0), eg. the object would hover in several locations cycling through sequential frames, obviously from an error with the matrix-to-euler conversion. similar curved and parabolic trajectories were observed though.. which could be due to my misunderstanding of class functions.

Edited by xoxos

##### Share on other sites

dealing with that embarassment changes it a little.. now the euclideanspace RH/RH system is yielding a diagonal response to left-right :)

regarding the use of pointers with class operators..

..in all of my projects i've never managed to have a more sophisticated need for pointers than a pointer to a pointer **p. i've always used them like i read in schildt, written &p or *p. accordingly i've never really understood code that places the & or * with the variable type and seem to remember someone saying it was only a different way of writing it. maybe it's not.

but now that i've correctly located the section in schildt about overloading operators (i mistakenly used the index to lead me to the operators, IO, overloading entry..) i can see i can use *this without having to declare anything in terms of address. so my class functions are now sensate. rotations still don't work though :)

##### Share on other sites

i don't like to think of forums as a place where my work gets done for me but i'm pretty much confounded on this issue. so, here's my source.

it confounds me that (even after discovering i had buggered myself with borrowing the overloading code) rotating works fine with 2d rotations, but even "plugging" someone else's "authoritative reference" matrix code in is utterly useless.

you can see it doesn't do much except draw one face of a tetrahedron on the screen, and then move it incorrectly :) only left-right arrows are implemented and rolls, which are enacted by pressing the opposite arrow key when the first is held. (there is no vertical/pitching movement yet).

the game loop is in game.h, the matrix function is at the end of vectoralg.h
(the rotation of the tetrahedron is slightly above the matrix declaration)

it's hard to describe exactly how it's wrong without seeing it, but it's definately wrong!

##### Share on other sites

..the darndest thing is that, as you move increasingly off-center to the left or the right, you'll see the path curves tightly around, *then it starts making the tetrahedron glide on it's own..*

...which is really wrong..... the gliding is built into relaxing the rotation speed vector..

..it doesn't glide when it's near the center, but it *does* glide when it's deviated from it...

now tell me, because i'm sure i understand at least this much....

if i multiply one rotation with a unity/origin matrix, it shouldn't move at all!!!! right? right!

isn't there some way i can do this without matrices?

Edited by xoxos

##### Share on other sites

switched to rotating the tetrahedron in front of a fixed camera to hopefully gain perspective.. pleasingly, there is a greater degree of stable performance.. one may go through a few offset rotations (only implemented arrow keys atm so 2d) before it will start to flip between two positions every other frame.

..fortunately, there are only two locations, which means my implementation has improved as previously aliasing was more of a field of locations.

also using a slightly different matrix, from http://www.geometrictools.com/Documentation/EulerAngles.pdf

their LH/LH x/y/z matrix is slightly different from mine (which is decommented) but both have about the same functional quality..

here's the matrix [abc, def, ghi] to angle conversion:

            if (c < .99999f) {
if (c > -.99999f) {
param.x = atan2(-f, i);
param.y = asin(c);
param.z = atan2(-b, a);
}
else {
param.x = -atan2(d, e);
param.y = -pih;          // half pi
param.z = 0.f;
}
}
else {
param.x = atan2(d, e);
param.y = pih;
param.z = 0.f;
}


the angle to matrix here is:

yc*zc                     -yc*zs                    ys

xs*ys*zc + xc*zs     xc*zc - xs*ys*zs      xs*-yc

-xc*ys*zc + xs*zs   xs*zc + xc*ys*zs      xc*yc

there is obviously some misconversion as the angle will start to spin rapidly when incrementing through some angles.. so i am wondering, perhaps i have trusted the wrong source for my matrix.

replaced source, if anyone's up for extra benevolent fun.. http://xoxos.net/temp/source.zip
matrix is at the end of "vectoralg.h" and it's used in "game.h"

Edited by xoxos

##### Share on other sites

anyone have any thoughts on this? it seems like it's "mostly right" which is of course useless.

it seems stable, but as the rotation grows more complex, it begins to exhibit inaccuracies which switch between two values. the scale of this inaccuracy varies, so that continuing rotation might result in indiscernible aliasing, or again stable performance, so i anticipate that it is something to do with the matrix to angle stage.

is there a matrix-to-angle conversion set anywhere you can recommend that i *can* trust?

Edited by xoxos

##### Share on other sites

...here's a clue that can reduce the problem to something that can be addressed textually -

i applied the rotation with euler angles and removed the matrix multiplication. the rotation is still converted to matrix form and converted back to radians ;)

by doing this i was able to observe that everything seems to be working fine with the conversion steps except that the y axis is "mirrored" at +90/-90... if i wished to continue to turn around the y axis in one direction, i have to switch arrow keys to do it when i hit these points, every 180 degrees...

this fits with the bilocating aliasing, and some of the strange rotation paths i was getting had a 2nd order wobble to them, which the reflection would explain..

..still haven't figured exactly what's happening to the coefficients to cause this..

##### Share on other sites

crossing +/- pi/2 is doing "some fun gimbal thing", adding or subtracting pi to x and z. anyone have insight into prudent schemes to test the outcome and adjust accordingly?

almost there

##### Share on other sites

aha...

the problem is that asin returns in the +/- pi/2 range, which is why we are returning incorrect results for the other hemisphere!

amazing how this has manifested in the various implementations..

so.. i only need to identify these cases, easy to fix..

..next is, so how do i do that...

##### Share on other sites
I wonder why you didn't ditch the three-angles representation two weeks ago. You could have saved yourself a ton of work.

##### Share on other sites

audio dsp :) radians are how i think about and use numbers. i have begun to appreciate the statements for keeping things in matrix form, but radians are conceptually "what i'm doing" in more than a "avoiding matrices" way.

(..had to type that three times now due to interruptions, page disappears whenever i put it down..)

..so now i see the fine print for 3 x 3 matrices.. one angle will be limited to a hemisphere due to the asin().. i had not discretised this from my earlier reading.. ..and i haven't identified any strategies for using a full sphere yet, but i am sure there must be some otherwise matrices would hardly be used.

##### Share on other sites
Imagine someone would ask you a question about audio dsp and he insists on representing angles as slopes. So if you want to add an angle to another angle and you represent them both using radians, you just do x+y and you are done; but if you represent angles as slopes, you need to do (x+y)/(1-x*y), or some such nonsense, and things kind of break down when the slope is infinite... Wouldn't you strongly recommend that he become familiar with the notion of angle and be done with it? (Actually, I suspect unit-length complex numbers are a better choice than angles for many situations in audio dsp, but I don't have any real experience in that field.)

Well, similarly you should consider that either with matrices or quaternions, composition of rotations is simply x*y, and you should really consider using one of the better representations.

..so now i see the fine print for 3 x 3 matrices.. one angle will be limited to a hemisphere due to the asin().. i had not discretised this from my earlier reading.. ..and i haven't identified any strategies for using a full sphere yet, but i am sure there must be some otherwise matrices would hardly be used.

I have no idea what you are talking about. 3x3 matrices don't have any such limitations.

##### Share on other sites

I have no idea what you are talking about. 3x3 matrices don't have any such limitations.

this matrix-to-radian method is much like others i see -

            if (c < .99999f) {
if (c > -.99999f) {
param.x = atan2(-f, i);
param.y = asin(c);
param.z = atan2(-b, a);
}
else {
param.x = -atan2(d, e);
param.y = -pih;
param.z = 0.f;
}
}
else {
param.x = atan2(d, e);
param.y = pih;
param.z = 0.f;
}


asin() returns values from -pi/2 to pi/2, so only a hemisphere using a naive conversion.

the only concept i have for converting this to a +/-pi range for y is to check if z has changed by a significant amount, then subtract pi from x and z and set y to pi - y. i'd thought there's probably a more informed way that people used to thinking about calculus might use to check

if i weren't here typing this, i would have been busy writing my app in radians, doing the things i've thought about doing, even the nescient way i'm a very "outwards" person, i do try to avoid learning whenever possible because i enjoy demonstrating what is possible without reference, with simple conception but this whole rotation thing is a big dent in the difference between what i can conceive and what i am able to realise. really - xoxos.net, audio synthesis guy for many years. understand that you may use numbers to represent space, i may be using spacial numbers to represent sound, and my app may have very few graphic elements compared to the elements more appropriately parameterised by radians

stupid stuff.. http://xoxos.net/blewm.zip

Edited by xoxos

##### Share on other sites

someone did a screen capture of blewm so you don't have to mess up your stuff..