PDA

View Full Version : Functions for Quaternions?



12-24-2004, 09:39 AM
Just wondering if there where any Rotation function that accepted Quaternions in GL, for matrix construction, and Quat math.

Or is this all done outside of OpenGL and must obtain a seperate library? If so anyone, have a suggestions?

dvm
12-24-2004, 01:18 PM
Nope, in OpenGL you only get glRotate, glTranslate and glScale. You'd have to do all the math yourself and then do the proper transformations. Furthermore you have glLoadMatrix, glMultMatrix which either load a 4x4 matrix you specify or multiply the current matrix with the one you provide. (attention: matrix is gives as a 16 position column matrix). So if by using your math you come to a 4x4 matrix you don't have to use glRotate, etc.
I haven't heard of a quaternion library but a google search might enlighten you. Best of luck and Merry Christmas!

01-01-2005, 09:21 AM
Quaternions can be represented as a four element vector. Quaternion multiplication (which is the most frequently needed operation) can be represented as q1 * J * q2, where q1 and q2 are vectors (rank 1 tensors) and J is a rank 3 tensor (or three dimensional array) comprised of 1s, 0s and -1s.

Anyways, the point of all that is that q1*J is a 4x4 matrix, and J*q2 is a 4x4 matrix. So in some circumstances it can be worthwhile using opengl's 4x4 matrix multipliction to implement quaternion multiplication.

If I recall correctly, these matrices look like this:

q1*J (where q1= <w,x,y,z>)

w -x -y -z
x w -z y
y z w -x
z -y x wJ*q2 (where q2= <W, X, Y, Z>)

W X Y Z
-X W -Z Y
-Y Z W -X
-Z -Y X W

woc
01-01-2005, 09:22 AM
Quaternions can be represented as a four element vector. Quaternion multiplication (which is the most frequently needed operation) can be represented as q1 * J * q2, where q1 and q2 are vectors (rank 1 tensors) and J is a rank 3 tensor (or three dimensional array) comprised of 1s, 0s and -1s.

Anyways, the point of all that is that q1*J is a 4x4 matrix, and J*q2 is a 4x4 matrix. So in some circumstances it can be worthwhile using opengl's 4x4 matrix multipliction to implement quaternion multiplication.

If I recall correctly, these matrices look like this:

q1*J (where q1= <w,x,y,z>)

w -x -y -z
x w -z y
y z w -x
z -y x wJ*q2 (where q2= <W, X, Y, Z>)

W X Y Z
-X W -Z Y
-Y Z W -X
-Z -Y X W

01-02-2005, 04:25 PM
glRotatef(GLfloat angle,
GLfloat x,
GLfloat y,
GLfloat z)

Accepts what is essentially a Quaternion: an angle and the vector for an axis of rotation, this is most likely to avoid gimbal lock.

Rob The Bloke
01-03-2005, 11:53 PM
Originally posted by <Jon B>:
glRotatef(GLfloat angle,
GLfloat x,
GLfloat y,
GLfloat z)

Accepts what is essentially a Quaternion: an angle and the vector for an axis of rotation, this is most likely to avoid gimbal lock.nope! you're getting confused between axis-angle rotations, and quaternions. Putting quaternion data into glRotatef is only going to lead to trouble

iznogoud
01-04-2005, 05:35 AM
Rob is correct. The only ogl call that makes sense
when using quaternions is glMultMatrix (or glLoadMatrix).

There is no point in using the Euler angles (and the
calls glRotate) if you have quaternions in place (for
viewing that is, placing objects in the scene can
be a mix of both).

I am willing to be that if you search these forums
for quaternion, you will find many responses in the
tone of "ditch the Euler angles." In fact, you should
search the forums and pick up the links to learn all
about quaternions.

cyclone
01-04-2005, 06:54 AM
But why haven't OpenGL some minimal quaternion support ???

OpenGL have already things such as glRotate, glTranslate and glScale that can **always** be make with "more generals" OpenGL funcs such as glMultMatrix or glLoadMatrix :)

So, if OpenGL have already things such as glRotatef, glScalef and glTranslatef, I can't see any reals reasons to not have something such as glMulQuat or glLoadQuat ...

And, after some research, it's seem that alls matrix*matrix computations are done on the CPU, only vector*matrix computations (cf. T&L vertex transformations) are really hardware accellerated, so here too any valids reasons to not include something such as glMultQuat and glLoadQuat into OpenGL ...

@+
Cyclone

V-man
01-04-2005, 07:09 AM
Hi cyclone,


Originally posted by cyclone:
But why haven't OpenGL some minimal quaternion support ???
I guess they neglected adding it.
Maybe when hw supports it, an extension will be created.

I'm adding quat support for my glh library
but it's not uploaded yet.

If you want to try it, I'll upload a beta version.

cyclone
01-04-2005, 07:28 AM
>I guess they neglected adding it.
>Maybe when hw supports it, an extension will be
>created.

They **don't want** to make it !!!
Why ? I don't understand ...

They are already a lot of posts/forums about one hypothetic OpenGL quaternion support and this always finish by something such as : "this have nothing to do into the OpenGL lib" ...

But this is **really** something related to a 3D graphic library ... :)

>I'm adding quat support for my glh library
> but it's not uploaded yet.
>If you want to try it, I'll upload a beta
>version.

Thank but I have already make two funcs that I have **very logically** named glLoadQuatf and glMultQuatf :)

But I can always (and want) testing your glh library :)

@+
Cyclone

plasmonster
01-04-2005, 12:51 PM
OpenGL requires a standard 4x4 matrix to transform points; the hardware is designed to do this transformation very efficiently. Quaternions, on the other hand, are inferior in this regard, requiring nearly twice as many operations as a standard matrix transform.

So in asking for quaternion support, you're essentially asking the GL to provide you with a "QuaternionToMatrix" function, which you could, and likely should, write yourself. If you use quaternions in your work, then you should probably have a quaternion library.

Don't expect the GL to do your math for you; that's not what it was designed for. Arguments for features of this nature invariably lead to a slippery slope, on which none can find good footing ;)

By the way, I don't think that JonB meant to imply that axis-angle <==> quaternion, perhaps only that they bear a strong resemblance :)

cyclone
01-04-2005, 04:20 PM
>OpenGL requires a standard 4x4 matrix to
>transform points; the hardware is designed to do
>this transformation very efficiently.

Somes transistors can be added for handle
quaternions computations ...

>Quaternions, on the other hand, are inferior in
>this regard, requiring nearly twice as many
>operations as a standard matrix transform.

It's true :(
But hardware accellerated, the difference can be relatively amortised ...

>So in asking for quaternion support, you're
>essentially asking the GL to provide you with a
>"QuaternionToMatrix" function, which you could,
>and likely should, write yourself.

No problem for me :) (I only loose a little time)
But for **the majority** of others ???

>If you use quaternions in your work, then you >should probably have a quaternion library.

Only on my spare time :(
And two funcs doesn't make one big library :)

>Don't expect the GL to do your math for you;

Personnaly, I use OpenGL because it can make some
maths such as rasterize Gouraud/Phong per pixel
triangles/quads/polygons, shading with multiples
lights, a big resolution and a lot of detailled animated objects in real time ...
=> a **lot** of 2D/3D/4D maths computations ...

And you ?

>that's not what it was designed for.

OpenGL was designed for to make a lot of things !
In fact, OpenGL's world generally **vote** for to add something into the GL core.
Here (cf. this forum), it's generally one [hardware ?] dictature ...

>Arguments for features of this nature invariably
>lead to a slippery slope, on which none can find >good footing ;)

This is exactely the type of arguments that make that the lack of this feature have several years.

And can never be closed if nobody make something
for to **advance** in this topic ...

>By the way, I don't think that JonB meant to
>imply that axis-angle <==> quaternion, perhaps
>only that they bear a strong resemblance :)

You can see a matrix such as :

16 floats (or doubles)
4 colums x 4 rows
4 axis vector (4D vectors X, Y, Z and W)
1 matrix

Personnaly, with the X,Y,Z vector representation and something like the complex i*i=-1 in each vector and the usuals cos/sin tricks for rotations, I **suspect** (i'm not a good mathematician) the quaternion idea ...

And you, how "see" you a quaternion ?

@+
Cyclone

cyclone
01-04-2005, 07:26 PM
In fact, I have a day to dream about something that can be implemented as this :

int gl2999Normal3f(float x, float y, float z)
{
gl_Normals[++gl_numNormals] = GLV3F(x,y,z);
return gl_numNormals;
}

int gl2999Color3f(float x, float y, float z)
{
gl_Colors[++gl_numColors] = GLV3F(x,y,z);
return gl_numColors;
}

int gl2999Vertex3f(float x, y, z)
{
gl_Positions[++gl_numPositions] = GLV3F(x,y,z);
if(gl_in_immediate_mode) gl2999PushVertex();
return gl_numPositions;
}

int gl2999VertexInstance(int v, int t, int n, int c, ...){
++gl_numVertices;
gl_Vertices[gl_numVertices].v=gl_Positions[v] * gl_PositionMatrix;
gl_Vertices[gl_numVertices].t=gl_Texels[t] * gl_TexelMatrix;
gl_Vertices[gl_numVertices].n=gl_Normals[n] * gl_NormalMatrix;
gl_Vertices[gl_numVertices].c=gl_Colors[c] * gl_ColorMatrix;
...
return gl_numVertices;
}

int gl2999PushVertex()
{
return gl2999VertexInstance(gl_numPositions, gl_numTexels, gl_numNormals, gl_numColors);
}


This can resolve some problems such as :

"extended immediate indexed vertex mode"
instanciations

And I don't really see in this code where can be the bottleneck or what is really the hardware problem (if we have a form of "opengl memory" that can be automatically paged/swapped by the OS)

But "no, no, no => hardware cannot and this is a crime to make this in the OpenGL core"

But now in 2005, instantations are here !!!! :)
But not in OpenGL ...
And direct access to the "3D video memory" is always a nightmare in OpenGL :(

Now, it's about the same thing about 1) quaternion support and 2) nobody want respond about an hypothetic raytracing support ...

So OK, OpenGL isn't really as "open" as this ...

So I prefer close this type of discussion and create myself a good 3D API for my PocketPC :)

@+
Cyclone

Rob The Bloke
01-04-2005, 09:08 PM
this may end up being long, apologies beforehand. Some of what i say here may not be exactly mathematical, but it's an easy way to visualize quaternions... (I recommend reading the maths book, flat land, it's very good at explaining higher level dimensions in easy to understand language).

(I Don't want to fill this with C code, so check out the matrix & quaternion FAQ (http://skal.planet-d.net/demo/matrixfaq.htm) . Any examples i use here will use the funcs as they are listed there - saves me a bit of time.... I will kinda add to it a bit though)

First off. get a piece of paper and draw a little picture. The image you create is 2D, however you have used 3D to do it - don't believe me? If you had used 2 Dimensions to draw the picture, you'd have never taken the pencil off the paper. Try drawing the same picture again but this time, keep your pencil on the page at all times. It suddenly becomes more difficult.

In effect, to make life easy when creating a 2D image, we add the third dimension to give us much more subtle control over the tools we are using.

Right then, apply the same concept to 3D graphics. You will notice that we use a 4x4 matrix to transform points, not a 3x3. Without the 4th dimension, moving objects about in space becomes far more complicated - in effect we are 'keeping the pencil on the page'. Handily, this is easier to understand when looking at a quaternion....

ok, so we have a quaternion defined with {x,y,z,w}. In 3D graphics we only ever use normalised quaternions. Normalising a quaternion is exactly the same as normalising a 3D vector, except you use 4 components, not 3. Therefore, the quaternions we are interested in will always sum to 1...

consider the following quaternions,

1,0,0,0 - rotation of +180 degrees around x
-1,0,0,0 - rotation of -180 degrees around x

0, 1,0,0 - rotation of +180 degrees around y
0,-1,0,0 - rotation of -180 degrees around y

0,0, 1,0 - rotation of +180 degrees around z
0,0,-1,0 - rotation of -180 degrees around z

0,0,0,1 - no rotation at all...

In effect, as the w value increases, our 3D pencil 'lifts' off the page, thus it has no affect on the objects. You will see that 0,0,0,1 basically is an identity. You may also notice that we never use the quaternion, 0,0,0,-1. This makes no sense, we only lift the pencil UP off the page, we don't want to force it through the paper because that would make a nasty hole.... ;)

Now, consider again the quats, {1,0,0,0} and {-1,0,0,0}. Essentially they are almost the same, they will be 180 degrees around the x axis... The difference here, is that the sign indicates a direction of rotation, ie which way we will rotate around the x axis.

Consider making a character rotate from 0 to 270 degrees, we could equally rotate from 0 to -90 degrees to get to the same position. The problem with euler angles is that we'd probably end up rotating the long way around rather than the shortest route. Normally the shortest rotation is the one we want.

It's pretty much impossible to calculate the shortest rotation using euler angles... every year one of my students trys to prove me wrong... they never do... ;)

The handy thing with quaternions is that any quaternion can easily be negated to change the rotation direction. ie,

void quaternion_invert( QUAT* q ) {
q->x = -q->x;
q->y = -q->y;
q->z = -q->z;
// leave w alone ! don't want it making a hole in our paper!!!
}This is by far the most important characteristic of quaternions, but more on that later.... The inverse is also called the conjugate...

Right then, how do we add two quaternions? ie, combine 2 quaternions into a single quaternion that represents both?

the answer is that we don't add them ;) infact, they behave much more like matrices than vectors, so basically we multiply them together....

q_total = q1 * q2

This is kinda useful. Knowing how to invert and mutliply quaternions, should help you do most things you need to with rotations.... for example, lets assume we have q1 and q2. If we wanted to find the quaternion that will go from q1 to q2, we can use this....


quaternion_difference(QUAT* diff,QUAT* q1,QUAT* q2) {
QUAT q1_inverted;
quaternion_conjugate(&amp;q1_inverted,&amp;q1);

// almost like saying... diff = q2 - q1
quaternion_multiply(&amp;diff,&amp;q1_inverted,&amp;q2);
}Now then, we are almost done. We WILL need a way to convert the quaternion to a 4x4 matrix so we can transform things. funcs like glLoadQuatf, glMultQuatf aren't actually that useful - normally we want to include translation and scaling with our transforms, ie what a 4x4 matrix gives us. Trying to actually transform things with quaternions is frankly a waste of time. Evaluate the quaternion, then convert to a matrix.... The reason is because of SLERPing.

On a personal note: I only ever store rotations as quaternions within my game engine code. It's pretty much pointless to store things in euler or axis angle rotations imo. Therefore, i only have a QuatToMatrix function, since when you transform the objects you WILL want a 4x4 matrix.... You may find that having AxisAngleToQuat() and EulerAngleToQuat() functions is useful. I can't see the benefit though :|

With animation, we will want to gently rotate between two rotations over a number of seconds, and as already mentioned, we want to find the shortest rotation path so that this looks *correct*. The process for this is as follows, given the quaternions,

{1,0,0,0} and {0.9,0,0,0.1}

we dot product them to see if they are similar, ie


float quaternion_dot_product(QUAT* q1,QUAT* q2) {
return q1->x*q2->x + q1->y*q2->y + q1->z*q2->z + q1->w*q2->w;
}If the answer is +ve then they are fairly similar. If the answer is negative, then we have a rotation greater than 180 degrees to perform. Confused?

Q1 = { 1,0,0,0}; // +180
Q2 = {-1,0,0,0}; // -180

Q1 dot Q1 will give us 1. ie, they are the same.
Q2 dot Q1 will give us -1.

from Q1 to Q2 we have a rotation of 360 degrees. Chances are, that we really want the rotation to stay as it is rather than do a 'full 360'. To solve this, we can simply invert Q2, now we have...

Q1 = { 1,0,0,0}; // +180
Q2 = { 1,0,0,0}; // +180

This is now the shortest rotation between them since they are now both the same.... ;)

Usually we see linear interpolation written as,

Result = (1-t) * a + t * b; // or
Result = a + t * (b-a);

as t varies from 0 to 1, a linear transition occurs from a to b. This is great for scale and translation, but isn't that good for rotations.

Consider a sphere for a moment, and on that sphere create 2 dots. We effectively want to move from point a to b. If we used linear interpolation, we would move from a to b in a straight line - as i said, great for translation - thats exactly what we would want. With rotations however, we would want to scribe an arc from a to b, ie follow the sphere's surface.

For this then, we need Spherical Linear Interpolation..... This basically hacks the equations to use cosine and sine rather than a strict linear change.

{EDIT: Matrix and Quaternion FAQ is missing the SLERP function, so i've pulled the one out of my maths lib. Uses some operators instead of funcs, but i reackon you'll get the idea...}


/// a function to spherically interpolate the two quaternions
//
XQuaternion SLERP(const XQuaternion &amp;q1,const XQuaternion &amp;q2,const float &amp;t)
{
// for some reason, though why I don't actually know, the second quaternion
// needs to be inverted if the angle between them is greater than 90 begrees.
// I think it is to control the direction of the angular rotation so that the
// bone always slerps through the shortest angle. Just guessing though so don't
// take my word for it....
//
XQuaternion Second;

// will hold the final position
XQuaternion Final;

// if two quarternions are the same
if ( FCOMPARE( q1.x, q2.x ) &amp;&amp;
FCOMPARE( q1.y, q2.y ) &amp;&amp;
FCOMPARE( q1.z, q2.z ) &amp;&amp;
FCOMPARE( q1.w, q2.w ) )
{
// no point in slerping them....
return q1;
}

// perform dot product
float product = ( q1.x * q2.x ) +
( q1.y * q2.y ) +
( q1.z * q2.z ) +
( q1.w * q2.w );

// if the angle is greater than 90degress, negate quaternion and product
if ( product < 0.001f )
{
Second = XQuaternion(-q2.x,-q2.y,-q2.z,-q2.w);
product = -product;
}
else
{
Second = q2;
}

// calculate the linear t values for the interpolation
float interpolating_t = t;
float interpolating_invt = 1.0f-t;

// acos blows up if product becomes > 1 due to rounding error
if(product > 1)
product=0.999999f;

if(product < -1)
product=-0.999999f;

// spherical interpolation takes place around a sphere. Therefore we use the
// product (remeber dot product to finds angle between 2 vectors) to get the
// angle we have to rotate to go from q1 to q2.
//
float theta = static_cast<float>( acos(product) );

// precalculate the sin value
//
float sinTheta = static_cast<float>( sin(theta) );

if(sinTheta != 0.0f)
{
interpolating_invt = static_cast<float>( sin(interpolating_invt * theta) / sinTheta );
interpolating_t = static_cast<float>( sin(interpolating_t * theta) / sinTheta );
}

// perform the interpolation
Final.x = (interpolating_invt * q1.x) + (interpolating_t * Second.x);
Final.y = (interpolating_invt * q1.y) + (interpolating_t * Second.y);
Final.z = (interpolating_invt * q1.z) + (interpolating_t * Second.z);
Final.w = (interpolating_invt * q1.w) + (interpolating_t * Second.w);

Final.Normalise();

// return the result of interpolation
return Final;
}There is nothing else to know really ;)

Rob The Bloke
01-04-2005, 09:36 PM
Originally posted by cyclone:
And I don't really see in this code where can be the bottleneck or what is really the hardware problem
The slowness factor of immediate mode is down to a couple of things.

1. Function call overhead
2. The data has to be fully expanded so that a model with say 1000 triangles but only say 600 vertices, would end up having to transform 3000 vertices. a 5X increase in the workload.

Ideally if you specify all vertex data in a single array, openGL can transform all of it in one go. This then requires that we have a way to index these into faces much further down the pipeline - ie, construct the faces AFTER the TnL stage. This means we have to prepare the data and send it.

The downside is that for OpenGL to TnL vertex data, we need to know what the UV coord, material colour and normal vector is for each vertex when it's transformed. This means each vertex MUST be sent with that data.

To use seperate indices for Verts, Normals, Uv's, Colours etc would always result in us expanding the data.

Vertex Buffer Objects are about as good as you can possibly get, once you can handle vertex array formats, you realise that they are actually OK. I do have some issues with people trying to put everything into the GPU though, some things really dont belong there. Skinning and parametric surface calculation are two examples of things that can be done quicker in software using lazy evaluation and caching.

V-man
01-04-2005, 09:38 PM
Quat support has just been added, but not fully tested

http://www.geocities.com/vmelkon/glhlibrary.html

Cyclone, I guess if you are developing for Pocket PC, then this won't work for you.

plasmonster
01-05-2005, 08:43 PM
Cyclone, I wish you luck with your Pocket PC :)

cyclone
01-13-2005, 02:48 PM
Thank, Sgraham :)

Thank too Rob, I have only move the vector*matrix operations from the "hight level" gl2999VertexInstance to the "low levels" gl2999Vertex3f, gl2999Color3f, gl2999Texel3f and gl2999Normal3f funcs ...

With the actual OpenGL Vertex Array, you have to recompute this vector*matrix operation for **EACH** vertex (cf. for the color, the position, the normal and the texel), you never **cache** this computation ...

Why I speak is only that glVertex*, glColor*, glNormal*, glTexCoord* funs can return one index :)

That we can **reuse** some time after ....

And we can always ignore this return value if you don't want this value ...

Ok, it's not really a "true instanciation" but on my PocketPC, this is **REALLY** more speed :) :)