By: Yosi Hakel (t0xin) - 2008-02-28 19:29 |

Hi, I would really appreciate it if you could help me out regarding the lookAt() function here https://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm My situation is very simple My camera is located at point (0,0,0) I want to point it towards point (a,b,c) (i.e. orient the camera such that it looks at it) My only means of pointing/orienting the camera are EULER ANGLES (this is dictated to me by third party libraries which I cannot change) so my approach is very simple 1) Build a lookAt matrix using the code from said link: lookAt(double a, double b, double c, matrix& m) { vector3 up(0, 1, 0); vector3 z(a, b, c); z.norm(); vector3 x( up * z ); // x = up cross z x.norm(); vector3 y( z * x ); // y = z cross x m.set_components(x,y,z ); } 2) Convert the lookAt matrix to Euler angles using the code from https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm : rotate(matrix m) { if (m.m10 > 0.998) { // singularity at north pole heading = Math.atan2(m.m02,m.m22); attitude = Math.PI/2; bank = 0; return; } if (m.m10 < -0.998) { // singularity at south pole heading = Math.atan2(m.m02,m.m22); attitude = -Math.PI/2; bank = 0; return; } heading = Math.atan2(-m.m20,m.m00); bank = Math.atan2(-m.m12,m.m11); attitude = Math.asin(m.m10); } But the Euler angles I get don't make sense to me For example, using this code for the vector (0,0,1) (i.e. standing at (0,0,0) trying to look at (0, 0, 1)) produces: heading = attitude = bank = 0 Whereas I'd expect : heading = +/-180 degs attitude = 0 bank = don't care since we're basically looking in the direction of the Z axis Another example, trying to look at the vector (1,1,1) (again standing at (0,0,0)) produces heading = attitude = 0 bank= ~35 degs Again I don't understand it, I would expect something like heading = 135 degs attitude= -45 degs bank= don't care Since we're looking at a vector symmetrical to all axes (thus 45 and 90+45 degs) I'm probably missing something and/or doing something wrong, so again, I would appreciate your help Thanks a lot in advance, Ohad P.S. I am aware that there's a problem when the up vector (0, 1, 0) is parallel to the vector I want to look at, but I'll cross that bridge when I get to it (or at least try to...) |

By: nhughes (nhughes) - 2008-02-28 23:00 |

what software package are you using that only allows you to point your camera with Euler angles? Noel Hughes |

By: nhughes (nhughes) - 2008-02-28 23:15 |

Are you using the standard aerospace definitions of heading, attitude and bank angles? This convention defines the heading, or yaw, angle as the first rotation, about the +Z or vertical down axis, the attitude, or pitch, angle as the second rotation about the +Y or the "out the right wing" axis and roll as the third rotation about the +X or the "out the nose" axis. If so, looking at (0 0 1) will give you an attitude angle of -90, the roll and yaw angles will affect the orientation of the camera, but won't affect its line of sight. This assumes that the camera line of sight is defined as the camera X axis. If the camera line of sight is the Y axis, looking at (0 0 1) would be accomplished by a roll angle of 90 degrees. If the camera axis is the Z axis, no rotation is necessary, it is already looking at (0 0 1). I'd be glad to provide more insight into the problem if you can give me more details. Noel Hughes |

By: Yosi Hakel (t0xin) - 2008-02-29 01:26 |

The software package I use is called Tiltan TRLib/Tview: http://www.tiltan-se.co.il/page.asp?cat=183&lang=2&type=2 I get support from their chief programmer, so if there was a way to avoid Euler angles I'd know about it. But even if there were such way, I'd still need the Euler angles for the DIS (Distributed Interactive Simulation) broadcast (it's in the protocol, can't be changed). And the use of both the package and the standard is 100% set in stone. Regarding the standards, my apologies - I probably should have mentioned I'm using the same ones Martin does as described here: https://www.euclideanspace.com/maths/standards/index.htm They are the same as what you described, only the axis you call 'Z' I call 'Y' and vice versa. Also judging by your angles you seem to be using a left-handed coordinate system whereas Martin's standards assume a right handed one (I mean it in the sense that if the thumb of the right hand indicates the direction of the positive rotation axis then the fingers show the positive direction of rotation). Finally, the camera line of sight is indeed defined as the X axis. Please take a look at Martin's sample orientations at the bottom of: https://www.euclideanspace.com/maths/geometry/rotations/index.htm They illustrate said standards very well, just to be sure we're on the same page (since my explanation probably sucked :) ) Back to the issue at hand, in Martin's standards when I write (0,0,1) that means (0,1,0) in your standards, so you can see why I'd expect a heading of -90 (my previous writing of +/-180 was in error, I also meant heading = -45, attitude = 45 degs in the second example - sorry!), actually 90 in your case (since your system is left handed). The vector you think of as (0, 0, 1) is actually (0, 1, 0) for me, and yes I would expect an attitude of 90 (again this is the same as your -90 due to the right handed system) in that case. However, like I mentioned in my original post that specific vector is problematic with my chosen 'up' vector since it's parallel (identical, actually) to it, but I'd prefer to think about that singularity later, and solve the main issue first. Well, as you can see this is a little confusing, so perhaps you could indulge me and conform to Martin's standards in following messages? This matter is confusing enough for me as it is :) Thanks again for your help ! |

By: nhughes (nhughes) - 2008-02-29 14:27 |

The coordinate system I described is a RIGHT handed system! Thinking of an airplane, X forward through the nose, Y out the right wing - when you cross X into Y - fingers of the right hand start at X and curl into Y - your thumb is DOWN. If this software package uses a coordinate system as you describe and forces you to use only Euler angles, I hope you didn't pay good money for it. I recommend you dump it immediately. Good Luck. Noel Hughes |

By: Yosi Hakel (t0xin) - 2008-02-29 17:06 |

I meant right/left handedness in the sense of the positive direction of rotation about an axis. Just take a look at martin's https://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm https://www.euclideanspace.com/maths/standards/index.htm That's the same system I use In any case, like I said there is no possibility for me to dump that software and even if there were I'd still need to broadcast the Euler angles in the DIS protocol (which is even less likely to change). These are simply not my shots to call (I'm just a lowly programmer...) The thing is, using the code Martin put on his site my rotations (be it via quaternions or matrices) work great and so do the conversions back to Euler My only problem is with the lookAt function, and since it's on the site for many years I assume it does work, and it's just me who doesn't understand its proper usage... that's basically all I'm asking here (for now ;) ) Even if you don't know the answer to that, you still helped me understand the situation better through your questions, so I thank you for that I do hope, however, that someone here can help me with my original problem (Martin/Minorlogic?) Thanks again, Ohad |

Hi Ohad, If I understand Minorlogics code, what its doing is setting up 3 mutually perpendicular basis vectors to form a rotation matrix, so with the values you put in I would expect it to produce: z = (0,0,1) for your first example x = (0,1,0)x(0,0,1) = (1,0,0) y = (0,0,1)x(1,0,0) = (0,1,0) so this would give a matrix like: 1 0 0 0 1 0 0 0 1 which indeed is zero rotation I guess it must be assuming that the camera is already pointing along the z axis in the positive direction? second case: z = (1,1,1)/1.732 = (0.577,0.577,0.577) x = (0,1,0)x(0.577,0.577,0.577) = (0.577,0,-0.577) normalising gives (0.707,0,-0.707) y = (0,0,1)x(0.707,0,-0.707) = (-1,0.707,0) normalising gives (-0.816,0.612,0) so this would give a matrix like: 0.707 -0.816 0.577 0 0.612 0.577 -0.707 0 0.577 again I assume this would be the rotation of the camera from pointing along the z axis in the positive direction to point to 1,1,1 I may have made an error I'm very rusty at arithmetic (perhaps I should setup a javascript calculator for this on the webpage?) Do you get anything like this for the matrix? I haven't calculated the euler angles, as you say I would expect one of them to be 45deg but I don't think I agree with the 135 degs (because the far corner of a 1,1,1 box is further away). Martin |

By: Yosi Hakel (t0xin) - 2008-02-29 19:42 |

You're right, the angles I mentioned in my first post were wrong, they should be: First example - (0,0,1) heading = -90 attitude = 0 bank = anything Second example - (1, 1, 1) heading = -45 attitude = 45 bank = anything Regarding the camera looking along the Z axis, that still would not explain the results I got for (1,1,1), namely: heading = attitude = 0 bank = ~35 degs So I'm not really sure what's going on The interesting thing is that MinorLogic's code is the same as the internal code for D3DXMatrixLookatRH: http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c_summer_03/directx/graphics/reference/d3dx/functions/math/d3dxmatrixlookatrh.asp So I'm guessing the code is good, I just don't understand how to use it properly in my context |

I'm not sure I agree with the angles: heading = -45 attitude = 45 I think the angles should be more like: 45 and 35 Imaging you are in a cuboid room, you on the floor of one corner looking along along the base of one wall. You turn through 45deg and you are now looking at the base of the diagonal corner. You now want to look up to the top of the diagonal side. This forms a triangle where the adjacent side is sqrt(2) and the opposite side is 1. So the angle is arctan(0.7071)=35deg. So I think the 35deg is right, I suspect there must be a bug in your program for the heading and attitude. Are you getting the same matrix as I worked out? Martin |

By: Yosi Hakel (t0xin) - 2008-03-01 12:53 |

You're right, one would think I'd have the sense of not trusting my so called intuition in these matters. So the 35 degrees does make sense, I'll be in the office tomorrow and I will check my program and matrix and report back Thanks for your time, Ohad |

By: Yosi Hakel (t0xin) - 2008-03-02 20:31 |

Hi Martin, you were right on both accounts ! 1) I had a bug in my code 2) Once I fixed it, I indeed received values that would have been good if my camera were placed on the Z axis The interesting is that by switching the roles of the z and x axes in the code (simply replace any appearance of 'z' with 'x' and vice versa) the same code produces the right values for a camera lying on the X axis. Since your site assumes orientation zero is the X axis (all your examples show it like that) I kindly suggest you change the code that way if you get the time So a big thank you - but perhaps you could help me further ? The thing is, that code assumes the camera is currently with orientation zero (i.e. on the X axis).. but most times my camera would be in all sorts of orientations, and I want to know what's the orientation delta from the current orientation to the desired one. What I do now is multiply(from the left) said lookAt matrix by the inverse of the current rotation matrix. That way, when the current orientation matrix is multiplied by it, the desired final orientation is produced (because the current orientation is canceled out) and therefore it must represent the change.. I think ?? It seems to work, but even so, it doesn't strike me as exceptionally efficient so I was wondering if you had any thoughts on the matter. Another problem is that the code will always produce bank 0, but obviously I would want different banks in different situations. What I do now is, unoriginally, multiply the altered lookAt matrix (from the previous paragraph, the one that cancels out the current orientation), from the right this time, by the rotation matrix of my desired bank. The soundness of this step seems more likely, but at the very least it still seems inefficient. So just to make things clear, this is my final lookAt matrix: finalLookAt = inverse(currentOrientationMatrix) * lookAt * desiredBankMatrix; since multiplying the current orientation matrix by it produces the desired final orientation (including bank), I assume it must represent the "change" in orientation, and so I can interpolate/restrict that change according to angular velocities / gimbal limits I would gladly hear any insights you have into the matter Ohad |

Hi Ohad, I have changed the web page, at the moment I've only put a comment about assuming the initial orientation of the camera is along the z direction, I'm tempted to have 3 versions for initial orientations of x,y and z (representing side,front and top cameras). As you suggest it would be good to also have a version which allows any initial orientation and this can be worked out by: inputs: current target and required target. calculate lookat matrix for both these vectors. multiply required target lookat by transpose of current target lookat. I don't know if this could be simplified by having the 'up' vector the same in both cases, my instinct tells me that it should be possible to get some simplification! (danger signal) I'll have to think about it. As I understand it, it is the 'up' vector which determines the third angle, i.e. the angle around the axis that the camera is pointing along. I have problems getting a precise definition of this 'up' vector. I think it would be something like: the up direction when looking through the viewfinder of the camera. Martin |

By: Yosi Hakel (t0xin) - 2008-03-03 20:34 |

LOL, my example made it to the site :) RE the assumption of the initial orientation of the camera, I don't know if 3 versions are needed (It sure wouldn't hurt), but nevertheless I'd clarify the current explanation a bit more, maybe something along the lines of "if you want the initial direction to be along the x or y axis then swap their order when setting the components: m.set_components(z,y,x) or m.set_components(x,z,y) accordingly" just to be sure a layman would understand exactly what he needs to do (Just my personal opinion of course, And I might be dumber than the average user of your site :) ) I'll be in the office Wednesday and check MinorLogic's code for the rotation matrix between 2 vectors (hopefully the quaternion result too, the one for the lookAt diverged pretty bad for me), as well as your suggestion regarding the up vector Again your help is greatly appreciated, Ohad |

By: Yosi Hakel (t0xin) - 2008-06-05 14:48 |

Hi Martin, sorry for the late response ! First thing's first, regarding the 'up' vector given to the lookAt matrix version from https://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm Using a constant vector such as (0,1,0), which is the only recommendation I've ran across is no good. You end up rotating the bank axis for no reason, and near singularity points (attitude +-90) the camera spins wildly. My solution was using [currentRotationMatrix * (0,1,0)] as the up vector. It makes sure only the heading and attitude are changed for the lookAt so the bank is not altered, and it has stood up very well through testing. I highly suggest you mention it on the lookAt page. My current problem pertains to gimbal limits. That is, given the camera can only rotate HL degrees in the heading axis, AL degrees in the Attitude axis and BL in the bank axis, I need to make sure these limits are not crossed. The way I currently implement it is as follows: Whenever a rotation by some rotation matrix M is requested, I convert M to Euler angles (in your site's standards) - heading, attitude and bank, using the code from your site. If I understand correctly, they represent the change in each respective camera axis. For example, if a rotation by M is requested and the conversion of M to Euler produces heading=10, attitude = 20 and bank = 30, I conclude that the rotation requested is equivalent to the camera rotating 10 degrees in the heading axis, then rotating 20 in the attitude axis and filnally rotating 30 degrees in the bank axis. As such, I keep 3 variables: totalHeading, totalAttitude and totalBank, which I initialize as 0 and to which I add the delta of each such rotation. Following the previous exapmle, I would add 10 to totalHeading, 20 to totalAttitude and 30 to totalBank. If any of these variables reach the limit (e.g. abs(totalHeading)>HL), I know I've hit the gimbal limit so I restrict the offending axes to their respective limits and rebuild M from the restricted values. Finally I rotate by M. Firstly, I would be happy to hear your opinion about this algorithm for enforcing the gimbal limits. Secondly, even if you agree with it, a problem arises. Assume the camera is at HAB(=heading, attitude, bank) of 0,0,0. Now rotate the attitude by 90 degrees, then rotate the heading by 90 degrees, then rotate the attitude by -90 and finally the bank by 90 degrees. So you're in HAB=0,0,0 again, but totalHeading = 90 degs & totalBank = 90 degs. So basically your gimbals limits have "shifted". Is there a way to eliminate this shifting? Is it supposed to be like that? I'm a bit at a loss... Thank you for your time, Ohad Schneider |

Sorry it has taken me so long to reply, I’ve been on holiday and I’ve also been having computer hardware problems. >First thing's first, regarding the 'up' vector given to the lookAt matrix version from >https://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm >Using a constant vector such as (0,1,0), which is the only recommendation > I've ran across is no good. You end up rotating the bank axis for no reason, >and near singularity points (attitude +-90) the camera spins wildly. >My solution was using [currentRotationMatrix * (0,1,0)] as the up vector. >It makes sure only the heading and attitude are changed for the lookAt so > the bank is not altered, and it has stood up very well through testing. >I highly suggest you mention it on the lookAt page. Yes, thanks, I’ll put this on the page. I’ll have to think about why it works, after my holiday it takes a while to get my brain back in gear. >My current problem pertains to gimbal limits. That is, given the camera >can only rotate HL degrees in the heading axis, AL degrees in the Attitude >axis and BL in the bank axis, I need to make sure these limits are not crossed. The way that the Euler angles are defined on the site is as a sequence of 3 rotations, each one relative to the previous one, like a sequence of hinges, or (to remove linear movement) a gimbol made from concentric rings. So any limits would be relative to the previous rotations so I don’t think its useful to keep totals in the way you describe. There is an alternative way to define Euler angles, the nearest physical model I can think of might be a set of 3 grabbers so the first grabs the object and rotates it around the x axis and then releases it, then another grabs the object and rotates it around the absolute y axis and then release it, and so on for z axis. This would have different properties from the heading, attitude and bank model. But even if we use this second model, I still don’t think its useful to keep totals, because the rotation depends on the order of the rotations as well as the actual angles and I don’t think there is any way to change that. Martin |