Quaternion Rotations in 3D Java OpenGL

I created the tutorial “Quaternion Rotations in 3D Java OpenGL” to document a 3D math library that I wrote a few months ago. It includes an interactive demonstration applet and links to the javadoc and source code.
http://www.croftsoft.com/library/tutorials/quat/

Ooh, nice, Quaternions are difficult to understand, this tutorial looks good.

Thank you very much. It would be fine if you explained the way to use quaternions to avoid gimbal lock by using non eulerian transforms as many books and tutorials tell wrong things about it.

[quote]Thank you very much. It would be fine if you explained the way to use quaternions to avoid gimbal lock by using non eulerian transforms as many books and tutorials tell wrong things about it.
[/quote]
I always had trouble to understand what the exact problem with gimballock ?? and I still use euler angle for their simplicity.

I mean if you want to rotate an object around all its three axis you must also rotate its local axis the same way as the object and that’s all => no gimbal lock ?? dont know who invent the term of “gimbal lock” but it seems to be a confusion between world axis and local axis.

so when you have an object in 3d space you can set it in any 3D positions by using two Vector3D()
one for translation :
tx,ty,tz
one for rotation :
rx,ry,rz

than you set your object and its local axis by applying rx,rz,ry and translate it using tx,ty,tz

if you want to apply a rotation around x “world axis” and than around y “world axis” and etc… you simply apply your rotation wrx, than wry. and etc… but if you want to rotate around its local axis x than its local axis y you must keep in mind that the rx rotation will have also rotate the object local axis and you should not use a world rotation to achieve rotation in object space (there is the confusion I told aboout local and world axis), it is only valid for the first rotation when local and world axis are still “equals”.

if you want to rotate it around one of its local axis (so in object space) and you have no idea of the local rotation rx,ry,rz, you have to compute it using the above code and its local axis (you can create one on the fly choosing arbitrary axis x, axis y,axis z all perpendicular, but it is better to keep trace of object real local axis), than apply rotation you want and re-apply retrieved rotation of its local axis rx,rz,ry.

The problem is not really that a dimension of rotation is lost but rather that three rotation is too much to set an objet local axis, so you don really loose a dimension, you still have it, or rather “you dont need it”.

only euler, no quaternions, and no “gimbal lock”

the above code may help :
cax,cay,caz are the three vector3D that represent the local axis of an object in world space, (they must translated to the origine and normalized).

the function then give the three rotation that you must apply in the following order RX,RZ,RY to get the same axis.

		
double axx=cax.getX();		
double axy=cax.getY();
double axz=cax.getZ();
double ayx=cay.getX();		
double ayy=cay.getY();
double ayz=cay.getZ();		
double azx=caz.getX();	
double azy=caz.getY();
double azz=caz.getZ();
double den=Math.sqrt((axx*axx)+(axy*axy)+(axz*axz));
double sz=-axy/den;

		
double rz=0.0;

//floating value precision bug handle
if(sz>1.0) sz=1.0;
if(sz<-1.0) sz=-1.0;

rz=Math.asin(sz);
 
den=Math.sqrt((axx*axx)+(axz*axz));

//floating value precision bug handle
if(den>1.0) den=1.0;
double rx=0.0;
double ry=0.0;
if(Math.abs(den)>0.0);
{
	double cx=ayy/den;			
	if(cx>1.0) cx=1.0;
	if(cx<-1.0) cx=-1.0;
	rx=Math.acos(cx);
	if(azy<0.0)
	 rx=-rx;
	 			
	double cy=axx/den;			
	if(cy>1.0) cy=1.0;
	if(cy<-1.0) cy=-1.0;					
	ry=Math.acos(cy);
	if(axz<0.0)
	 ry=-ry;
}
		
if(Math.abs(rx)>Math.PI*0.5 && Math.abs(ry)>Math.PI*0.5)
{
	rx=-Math.PI+rx;
	ry=-Math.PI+ry;
	rz=Math.PI-rz;
}

NB.: the code above may have severals optimisations, let me know

EDIT:
for 3d modeler studio users (as 3ds max) => this is why when you rotate an object most of the time the three rotations are updated

Watch the FAQ about quaternions on www.developpez.net, I answered to this question. It is not easily possible to avoid gimbal lock with euler angles, I proved it scientifically some years ago and Pascal Mignot (teacher and researcher at the university of Reims) confirmed I was right:
http://jeux.developpez.com/faq/matquat/

Then, your engine can suffer of gimbal lock…

of course it is impossible as the “gimbal lock” term “is something logically wrong”, it is like saying when I do Math.sin(90) the result is not 1.0 so it is a wrong result ? no it isn’t if you think that the parameter should be given in radian.

the gimbal lock is the same as above, it is a confusion on space between object space operation and world space operation (confusion on unit in above example), read back the post I think it is well explained.

[quote] It is not easily possible to avoid gimbal lock with euler angles, I proved it scientifically some years ago and Pascal Mignot (teacher and researcher at the university of Reims) confirmed I was right:
[/quote]
hehe no, I no, there is no trouble with gimbal lock in my engine (the main reason is that it doesn’t really exist!), taking care of what I wrote above you can get ride of gimbal lock problem with euler… in short: you just have to keep in mind that when you perform an operation rotation/translation you modify the object local axis, it is as simple…

I don’t know what you mean.

Gimbal lock exists even though you wish to give this phenomenon another name. I agree with you, you have to preserve the local reference to solve this problem, that is what I wrote in the FAQ but I don’t see how your code solves the problem (it would be better with more comments). I debated with many people who suggested wrong solutions, for example using matrices instead of Euler angles, using quaternions but in the same way than Euler angles and I spent some time to prove that it fails, I hope I won’t have to do it again.

If you’re really sure you have no gimbal lock, show your piece of code to Pascal Mignot but I assume he might agree with me. Give a formal proof (a mathematic demonstration) that your method works as I did for mine some years ago.

[quote]Gimbal lock exists even though you wish to give this phenomenon another name. I agree with you, you have to preserve the local reference to solve this problem, that is what I wrote in the FAQ. But I’m not sure that your code solves this problem, I haven’t checked it mathematically.
[/quote]
ok if you want it exist, but it is not a problem or anything else that just a logical misundertood, that when you rotate one of three axis of an object (90°) it get parallel to one other axis in world space…

another thing is that you can perform arbitrary axis rotation using only euler :

Get an arbitrary axis in 3d space (the one you want to use to rotate around and represented by two 3d point)

V=P1-P2;

Compute its translation to the world origin : Tx,Ty,Tz

Compute the two world rotation rx & ry that you must apply to the world axis z so that it will then lie on your choosen rotation axis (like the code above, but only requiere to know rx,ry or rx,rz or ry,rz)

Once done, you can rotate any 3d point around this vector using the following sequence of rotation and translation.

V.translate(-T)
V.rotX(-rx);
V.rotY(-ry);
V.rotZ(rz); // here you are working in a snipet of local object space (not the whole one) you can apply any rotation you want around Rz and translation Tz, other rotations & translation are invalid as the local object space is not complete
V.rotY(rx);
V.rotX(ry);
V.translate(T);

[quote]I don’t see how your code solves the problem
[/quote]
I will make a draw/graphic, It is really simple and logic but hard to explain in simple-text.

add four vertex to your object with the following value :
o(0,0,0);
ax(1,0,0);
ay(0,1,0);
az(0,0,1);

wouaou a matrix !

once you will have performed any 3d operation on your object including rotation, translation and even zoom!! as many times as you want and in any order you wanted, you will still be able to get back the local object axis using the above code and the fourth new axis vertices, and so rotate around any of the three axis you want.

It can cause gimbal lock in some cases. You don’t prove anything, it is not a demonstration.

no… it can’t… you can set a vector looking in any direction with two rotations, apply those rotations in revers order and in the oposed sens you can make the inverse transform… damn… this is so logic, dont understand your “reticence”.

[quote]It can cause gimbal lock in some cases. You don’t prove anything, it is not a demonstration.
[/quote]
I am talking of world space rotation so you only rotate around (1,0,0), (0,1,0), or (0,0,1) no possibilities that two axis get merged. ok I have no time right now I will post a proper explaination tonight

See picture below

Object Local Axis is defined by four Points :
O (origine)
AX (Axis X)
AY (Axis Y)
AZ (Axis Z)

Full method to rotate around ax object axis
(but that is the same for any arbitrary axis),
some pass are ofcource unecessary but for educational purpose that’s better :

  • compute T : translation of O to world origin) => O as world origin == 0,0,0
  • compute R : rotation sequence RX,RZ,RY all applyed in WORLD SPACE

ok want to rotate around ax ? perform the following to the point you want to rotate :
let’s call the point to rotate P

//First get back in object space
P.Translat(-T)
P.RY(-ry);
P.RZ(-rz);
P.RX(-rx);

//Rotate point with desired angle
P.RX(arbitrary angle around RX)

//Go back to world space
P.RX(rx);
P.RZ(rz);
P.RY(ry);
P.Translat(T)

this is the long method…

knowing the local object axis in world space you
can easily set any vertices on this axis so you will
only perform transformation on the object local axis and then
use this axis for example to set vertice at render time.

below a sample code that rotate a point around an arbitrary axis
(starting at world orign, requiere only to add two translation -T and +T
for any arbitrary axis that not start at world origin)


public class Point3D
{

public double x,y,z;

public Point3D rotate(double angle,double x,double y,double z)
{
		
	//normalize axis in case of
	double n=Math.sqrt(x*x+y*y+z*z);
	if(n==0.0) return this;
	double in=1.0/n;
	x*=in;
	y*=in;
	z*=in;
		
	double nzx=Math.sqrt(x*x+z*z);

	double rx=Math.asin(y);
	double ry=0;
	if(nzx!=0.0)
		{
		ry=-Math.acos(z/nzx);
		if(x<0)
		ry=-ry;
	}
	else
	{	if(y>0)
			rx=Math.PI*0.5;
		else
			rx=-Math.PI*0.5;
		ry=0;
	}

	//Add here the -T translation for arbitrary axis (not starting at 0,0,0)
	this.rotateY(-ry);
	this.rotateX(-rx);
	this.rotateZ(angle);
	this.rotateX(rx);
	this.rotateY(ry);
	//Add here the +T translation for arbitrary axis (not starting at 0,0,0)
	return this;
	
}

//rotate around world x axis
public Point3D rotateX(double angle)
{
	double tY=y,tZ=z;
	double cosa=Math.cos(angle);
	double sina=Math.sin(angle);
	y=tY*cosa + tZ*sina;
	z=-tY*sina + tZ*cosa;
	return this;
}
	
//rotate around world y axis
public Point3D rotateY(double angle)
{
	double tX=x,tZ=z;
	double cosa=Math.cos(angle);
	double sina=Math.sin(angle);
	x=tX*cosa - tZ*sina;
	z=tX*sina + tZ*cosa;
	return this;
}
	
//rotate around world z axis	
public Point3D rotateZ(double angle)
{
	double tY=y,tX=x;
	double cosa=Math.cos(angle);
	double sina=Math.sin(angle);
	x=tX*cosa + tY*sina;
	y=-tX*sina + tY*cosa;
	return this;
}
}

not optimised, not tested too :slight_smile: but working code…

ex rotate PI/4 around the line l(t)=tx,ty,tz with x,y,z = 1, so we rotate 45° around an axis that start at 0,0,0 and finish at 1,1,1:
Point p=new Point3D();
p.x=200;
p.y=100;
p.y=50;
p.rotate(Math.PI*0.25,1.0,1.0,1.0);

p.x,p.y,p.z==rotated values

EDIT:

as the translation can be outsided so let’s rotate around a line that goes from P1 to P2 :
p.x-=p1.x;
p.y-=p1.y;
p.z-=p1.z;
p.rotate(Math.PI*0.25,p2.x-p1.x,p2.y-p1.y,p2.z-p1.z);
p.x+=p1.x;
p.y+p1.y;
p.z+=p1.z;

oki… better to add a “add” method…

public Point3D plus(Point3D p)
{
 this.x+=p.x;
 this.y+=p.y;
 this.z+=p.z;
}

and a sub one

public Point3D sub(Point3D p)
{
 this.x-=p.x;
 this.y-=p.y;
 this.z-=p.z;
}

finaly

p.sub(p1).rotate(Math.PI*0.25,p2.x-p1.x,p2.y-p1.y,p2.z-p1.z).add(p1);

below an online sample of rotating around arbitrary angle :

http://demo.dzzd.net/Viewer3D/

the object will rotate in the sens of the mouse drag , so around an axis that lie on the camera screen and wich is perpendicular to the user mouse drag, axis is computed each frame, so rotation is around a dynamic axis if you are not dragging in a straight line.

it achieved by the following :

  1. get the active camera
  2. put object axis in camera space
  3. rotate around the axis defined by user drag
  4. compute and update the object rotation rx,ry,rz

The term “gimbal lock” comes from the physical world. It describes the condition in gimbal systems where you lose one degree of freedom (i.e., the ability to rotate around one particular axis). In software it can be an issue when using a rotation system based solely on Euler angles if you don’t account for it. You may never see it, but it’s certainly possible since it’s an inherent characteristic (which quaternion rotations don’t have) of gimbal systems (simulated and physical). A real-world example of a near-gimbal-lock situation occurred on the Apollo 10 mission in May, 1969.

[quote]After Stafford’s camera failed, he and Cernan had little to do except look at the scenery until time to dump the descent stage. Stafford had the vehicle in the right attitude 10 minutes early. Cernan asked, “You ready?” Then he suddenly exclaimed, “Son of a bitch!” Snoopy seemed to be throwing a fit, lurching wildly about. He later said it was like flying an Immelmann turn in an aircraft, a combination of pitch and yaw. Stafford yelled that they were in gimbal lock - that the engine had swiveled over to a stop and stuck - and they almost were. He called out for Cernan to thrust forward. Stafford then hit the switch to get rid of the descent stage and realized they were 30 degrees off from their previous attitude. The lunar module continued its crazy gyrations across the lunar sky, and a warning light indicated that the inertial measuring unit really was about to reach its limits and go into gimbal lock. Stafford then took over in manual control, made a big pitch maneuver, and started working the attitude control switches. Snoopy finally calmed down.
[/quote]
I’ve read that there are several instances in the Apollo records and flight logs where pilots had to make special maneuvers to avoid gimbal lock. So yes, it’s a real problem, though the consequences aren’t severe in 3D graphics programming :slight_smile:

thank that’s very interristing, I will now try to read that to understand what it exacly mean :), as I still dont understand what the problem and when it can occur

EDIT: I got it :), this can happen when yaw, pich, roll are dependent each other as in the gimbal system , so I believe that’s not applyable when you are in a software and can magically rotate around anywhere without your axis behing really linked to something and then imediatly reorder all your axis wich is not possible in real life.

http://static.howstuffworks.com/gif/gimbal-2.gif

[quote]the left, you can see how each gimbal allows rotation around a specific axis. On the right, you can see a set of gimbals in gimbal lock. The inner-most gimbal can’t change in pitch unless someone puts the gimbals into another position.
[/quote]

It is not completely true. Gimbal lock is an inherent characteristic of eulerian transforms whatever you use (Euler angles, matrices, quaternions). The gimbal lock can be avoided by using non eulerian transforms that are easier to express with quaternions. That means that if you use quaternions with eulerian transforms (for example if you convert quaternions to matrices or Euler angles or if you combine them the same way as you already do with Euler angles), you can still have gimbal lock, I made a demonstration of it some years ago, my conclusions have been checked and approved, some FAQ reuse them now.

no, because can set an object in any position with euler angle … in real life there is some situation you wont be able to rotate in some direction, because in real life you cannot reorder your axis “immediatly”.

but in computer programming you can recompute you three rotation so that the object seems to have rotated in the direction you wanted to (any direction even the one that is impossible in gimbal shem above), wich is impossible in real life because it requiere that the object pass some other orientation before reaching this new position and that “immediatly”.

[quote]Euler angles are a means of representing the spatial orientation of any frame of the space as a composition of rotations from a reference frame.
[/quote]
and this is true, you can set an object in any position with the combinaison of three rotation, and in computer you can pass from one to another immediatly, wich is impossible in real life, that’s all !

the applet provided above that let you rotate the object in any direction use a combinaison of three rotation exacly as the gimbal system with three axis above, but all three rotations are re-computed each time, this cause jump from one pos to another some time but the object rotate smoothly and is rotated using ONLY three rotation (euler angles). you have a working sample, what do you want more, next week, I will add the three gimbal, to show the same gimbal system as above arround the rotated object in the applet. each of them representing rx,ry,rz and so you will be able to see those while smoothly rotating the object even when it is in the gimbal lock position.

EDIT:
note that then you can also compute any euler transformation between one posistion to another by doing : new rx- last rx,new ry-last ry,new rz - last rz. and I guess that when rotating an object and passing a gimbal lock pos some rotation should make a “big jump”, but who care… we are not in a real ship!!

Is it a demonstration? I worked 6 months on this problem and you seem not to understand what gimbal lock is as you said at the beginning of this topic. The problem comes from eulerian transforms. If you combine your rotation with this, there is a risk to obtain gimbal lock.

[quote]Is it a demonstration?
[/quote]
no but the following is enought for me : “no, because can set an object in any position with euler angle …” proof me that 1+1=2 ??

[quote] I worked 6 months on this problem and you seem not to understand what gimbal lock is as you said at the beginning of this topic.
[/quote]
maybe just explain me why it can be a problem in 3d software ??, that’s rather that that I dont understand…

[quote]The problem comes from eulerian transforms. If you combine your rotation with this, there is a risk to obtain gimbal lock.
[/quote]
and what is afraying in that ??? you will still be able to rotate in any direction…

this seems so simple that I dont understand we have to debat hours on this, so there could be two reasons : I dont understand what you said or you dont understand what I said, so please explain me in a short way a case where a gimbal lock can lock anything in software, or can cause a problem ??

let’s take a trivial case using rx,ry,rz. we take rx=90 & ry=90 so that we should be locked, what I said is just that we are not because we can still go in any other position.

it maybe as follow (but it depend on you choosen axis and sens for rotation to work with eg : x at right, x down, etc…, anyone can choose its own axis to works with) :

rx=90,ry=90,rz=0 == rx=90,ry=0,rz=90 == etc…

an object can be set in the same pos using differents combinaison for rotation, (this is why I said earlier that three rotation is too much.)

do you follow?? but even without that, if you agree with the fact that you can set an object in any 3d rotation position with euler angle (wich is true, no ?) you cannot disagree with the fact that you can go from one rotation pos to another at anytime, this is only logic… and trivial… and the way to go from one to another is just being able to rotate around an arbitrary axis, and you have one of the differents ways you can use to do that implemented in the code above.

No, that’s the point :frowning: you lose at least a degree of freedom. This is not a trivial problem, I needed some months to understand it, it was very difficult and plenty of people told me things that were in contradiction >:(. If you want, I can send you an extract of my report (in French ;D), maybe it could help you. I have a source code in C++ that implements the good formula (under GPL license).

Yes, you can adjust the angles at any time, clear and reset matrices and whatever you want. That doesn’t prevent gimbal lock from rearing its head, but can open up doors to avoiding it. But this isn’t an imaginary problem that goussej is making up. It’s a real issue that is common in 3D graphics. You’ll find it being discussed in a number of graphics books.

The point you seem to be missing is that those Euler angles need to be applied to the object you are rotating. Whether you let the graphics API handle it or you do it yourself, they are going to be applied one at a time. When rotations are performed sequentially, it’s possible for one axis to become aligned with another. The order in which rotations are applied can affect whether or not gimbal lock occurs, so there are ways you can avoid it. This page has a couple of suggestions. The main reason that you don’t see gimbal lock with quaternions is that the rotations are essentially applied simultaneously.