I want to use euler angles with opengl. Some people suggest something like:
glRotatef(Rx,1,0,0);
glRotatef(Rx,0,1,0);
glRotatef(Rx,0,0,1);
Of course if you rotate in more than one direction at a time this method completely falls apart. The better approach as far as I know is to convert everything to the format OpenGL expects (angle and a vector)
Euler angles to Quaternion is something that is covered rather extensively in a lot of different places on the internet. Unfortunately it seems every post on the internet has a different suggestion, and for the life of me none of them seem to work. It is very possible there is a suggestion that works, but it is lost to me in the long list of not quite working suggestions.
Following this suggestion has gotten me close. The rotations work in individual directions, but when combined they seem to misbehave. Currently my code looks like this:
double c1 = cos((Rx/180.0f*3.141592)/2.0f);
double c2 = cos((Ry/180.0f*3.141592)/2.0f);
double c3 = cos((Rz/180.0f*3.141592)/2.0f);
double s1 = sin((Rx/180.0f*3.141592)/2.0f);
double s2 = sin((Ry/180.0f*3.141592)/2.0f);
double s3 = sin((Rz/180.0f*3.141592)/2.0f);
double x = (s1*c2*c3-c1*s2*s3)*180.0/3.141592;
double y = (c1*s2*c3+s1*c2*s3)*180.0/3.141592;
double z = (c1*c2*s3-s1*s2*c3)*180.0/3.141592;
double w = acos(c1*c2*c3+s1*s2*s3)*2.0*180.0/3.141592;
//normalize vector, maybe not necessary?
double mag = sqrt(x*x+y*y+z*z);
x /= mag;
y /= mag;
z /= mag;
glRotatef(w,x,y,z);
Can anyone offer any suggestions on where to go from here?
The reason the first suggestion doesn't work is because it is rotating relative to the previous rotations. For example, the second line (assuming you meant glRotatef(Rx,1,0,0); glRotatef(Ry,0,1,0); glRotatef(Rz,0,0,1);) will rotate relative to the new y axis which has already been rotated by the first line. You want to rotate relative to the original axis.
Wolfram Alpha (http://mathworld.wolfram.com/EulerAngles.html) has a comprehensive description of Euler Angles, whatever representation you want to use.
One possibility would be to use the OpenGL Mathematics Library and use the extension. You can then use axisAngle rotations around each axis successively.
Related
I am trying to convert the orientation of an OpenVR controller that I have stored as a glm::vec3 of Euler angles into a glm::fquat and back, but I get wildly different results and the in-game behavior is just wrong (hard to explain, but the orientation of the object behaves normally for a small range of angles, then flips in weird axes).
This is my conversion code:
// get `orientation` from OpenVR controller sensor data
const glm::vec3 eulerAnglesInDegrees{orientation[PITCH], orientation[YAW], orientation[ROLL]};
debugPrint(eulerAnglesInDegrees);
const glm::fquat quaternion{glm::radians(eulerAnglesInDegrees)};
const glm::vec3 result{glm::degrees(glm::eulerAngles(quaternion))};
debugPrint(result);
// `result` should represent the same orientation as `eulerAnglesInDegrees`
I would expect eulerAnglesInDegrees and result to either be the same or equivalent representations of the same orientation, but that is apparently not the case. These are some example values I get printed out:
39.3851 5.17816 3.29104
39.3851 5.17816 3.29104
32.7636 144.849 44.3845
-147.236 35.1512 -135.616
39.3851 5.17816 3.29104
39.3851 5.17816 3.29104
32.0103 137.415 45.1592
-147.99 42.5846 -134.841
As you can see above, for some orientation ranges the conversion is correct, but for others it is completely different.
What am I doing wrong?
I've looked at existing questions and attempted a few things, including trying out every possible rotation order listed here, conjugating the quaternion, and other random things like flipping pitch/yaw/roll. Nothing gave me the expected result.
How can I convert euler angles to quaternions and back, representing the original orientation, using glm?
Some more examples of discrepancies:
original: 4; 175; 26;
computed: -175; 4; -153;
difference: 179; 171; 179;
original: -6; 173; 32;
computed: 173; 6; -147;
difference: -179; 167; 179;
original: 9; 268; -46;
computed: -170; -88; 133;
difference: 179; 356; -179;
original: -27; -73; 266;
computed: -27; -73; -93;
difference: 0; 0; 359;
original: -33; 111; 205;
computed: 146; 68; 25;
difference: -179; 43; 180;
I tried to find a pattern to fix the final computed results, but it doesn't seem like there's one easy to identify.
GIF + video of the behavior:
Full video on YouTube
Visual representation of my intuition/current understanding:
The above picture shows a sphere, and I'm in the center. When I aim the gun towards the green half of the sphere, the orientation is correct. When I aim the gun towards the red half of the sphere, it is incorrect - it seems like every axis is inverted, but I am not 100% sure that is the case.
32.7636 144.849 44.3845
-147.236 35.1512 -135.616
Those are the same. Left 33 or right 147. You are 180 from each other. Now look up 145 - that's past up that's 35 from horizon, your back is arched.
Now roll to get your back to the sky.
If you need to use Euler, try to keep pitch in -90 to +90, and roll in -180 to +180;
if (pitch > 90) {
pitch -= 90;
yaw += 180;
roll += 180;
}
if (roll > 180) {
roll = 360 - roll;
}
or something like that.
The definition of the any kind of 3 angles to represent a rotation is not only given by the order of the rotations, if they are extrinsic or extrinsic, but also which interval of angles you choose when you define the mapping of every element of the 3D Rotation Group to a tuple of 3 angles.
Unfortunately, it is common for software libraries to fail to explicit mention which subset of angles they support, so typically it is necessary to either test their behaviour or to direct inspect the source code. For a relevant issue regarding glm see https://github.com/g-truc/glm/issues/569, and see https://github.com/robotology/idyntree/pull/504 for a related discussion on another library on which I work.
In glm master, from a quick inspection of the code (https://github.com/g-truc/glm/blob/6543cc9ad1476dd62fbfbe3194fcf19412f0cbc0/glm/gtc/quaternion.inl#L10) and from the fact that in C++ asin image is roughly (-90.0, 90.0) and atan2 image is roughly (-180.0, 180.0), the assumed interval in glm seems to be roughly (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0), so by limiting the second angle (the yaw, using the names that you are using) to (-90.0, 90.0). So, what you are seeing at the GLM level is basically a mapping from the provided angles to equivalent angles in the (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0) range.
However, the fact that this angles are equivalent depends on how they are used, i.e. if you have a library that clamps the euler angles outside the used ranges, instead of converting it to equivalent angles, then you will obtain strange results. For this reason, I think it would be interesting to understand your problem to know how this angles are generated (the middle angles in particular seems to be part of the range (-90, 270) that is a strange, even if valid choice) and how they are interpreted to render the object in the visualization. Once you understand that, even if the rendering function works fine for angles in the original applications ranges, you can write a function to map "original application angles" to "GLM angles" and its inverse, that you can use for your original purpose.
Roughly following tony's advice and after some trial&error and pattern identification, I managed to figure out a way to restore the original values after the conversion.
ox, oy, and oz are the original pitch, yaw, and roll in degrees, before any conversion;
fx, fy, and fz are the new pitch, yaw, and roll in degrees, obtained after converting "Euler -> quaternion -> Euler" (via glm::degrees(glm::eulerAngles(glm::normalize(quaternion)))).
if (oy > 90.f)
{
fx -= 180.f;
fy -= 180.f;
fy *= -1.f;
fz += 180.f;
if (ox > 0.f)
{
fx += 360.f;
}
}
The above code seems to make the original angle values and the one after the conversion match exactly. While it answers the original question, it doesn't solve my actual issue... I was converting to a quaternion in order to smoothly interpolate to another angle. However, it seems that using glm::mix on the quaternion after the conversion results - again - in very unpredictable rotations.
So I am writing a game in C++, currently I am working on a 'Compass', but I am having some problems with the vector math..
Here is a little image I created to possibly help explain my question better
Ok, so as you can see the 2D position of A begins at (4, 4), but then I want to move A along the 45 degree angle until the 2D position reaches (16, 16), so basically there is a 12 distance between where A starts and where it ends. And my qustion is how would I calculate this?
the simplest way in 2D is to take angle 'ang', and distance 'd', and your starting point 'x' and 'y':
x1 = x + cos(ang) * distance;
y1 = y + sin(ang) * distance;
In 2D the rotation for any object can be just stored as a single value, ang.
using cos for x and sin for y is the "standard" way that almost everyone does it. cos(ang) and sin(ang) trace a circle out as ang increases. ang = 0 points right along the x-axis here, and as angle increases it spins counter-clockwise (i.e at 90 degrees it's pointing straight up). If you swap the cos and sin terms for x and y, you get ang = 0 pointing up along the y axis and clockwise rotation with increasing ang (since it's a mirror image), which could in fact be more convenient for making game, since y-axis is often the "forward" direction and you might like that increasing ang spins to the right.
x1 = x + sin(ang) * distance;
y1 = y + cos(ang) * distance;
Later you can get into vectors and matricies that do the same thing but in a more flexible manner, but cos/sin are fine to get started with in a 2D game. In a 3D game, using cos and sin for rotations starts to break down in certain circumstances, and you start really benefiting from learning the matrix-based approaches.
The distance between (4,4) and (16,16) isn't actually 12. Using pythagorean theorem, the distance is actually sqrt(12^2 + 12^2) which is 16.97. To get points along the line you want to use sine and cosine. E.g. If you want to calculate the point halfway along the line the x coordinate would be cos(45)(16.97/2) and the y would be sin(45)(16.97/2). This will work with other angles besides 45 degrees.
I'm having a problem understanding matrices. If I rotate my matrix 90 deg about X axis it works fine, but then, if I rotate it 90 deg about Y axis it actually rotates it on the Z axis. I guess after each rotation the axes move. How do I rotate a second time (or more) using the original axes? Is this called local and global rotation?
You don't "rotate" matrices. You apply rotation transformation matrices by multiplication. And yes, each time you call a OpenGL matrix manipulation function the outcome will be used as input for the next transformation multiplication.
A rotation by 90° about axis X will map the Y axis to Z and the Z axis to -Y, which is what you observe. So what ever transformation comes next start off with this.
Either build the whole transformation for each object anew using glLoadIdentity to reset to an identity, or use glPushMatrix / glPopMatrix to create a hierachy of "transformation blocks". Or better yet, abandon the OpenGL built-in matrix stack altogether and replace it with a proper matrix math library like GLM, Eigen or similar.
Add 'glLoadIdentity' between the rotations.
In practice best way to overcome this problem is to use quaternions, it is quite a bit math. You are right about; if you rotate it around Y 90 degrees than if you want to rotate it around Z you will be rotating around X.
Here is a nice source to convert euler angles to quaternions: http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/
And here is how to make a rotation matrix out of a quaternion:
http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/
After you have filled the matrix, you can multiply by calling glMultMatrix( qMatrix);.
Thinking about it last night I found the answer (I always seem to do this...)
I have an object called GLMatrix that holds the matrix:
class GLMatrix {
public float m[] = new float[16];
...includes many methods to deal with matrix...
}
And it has a function to add rotation:
public void addRotate2(float angle, float ax, float ay, float az) {
GLMatrix tmp = new GLMatrix();
tmp.setAA(angle, ax, ay, az);
mult4x4(tmp);
}
As you can see I use Axis Angles (AA) which is applied to a temp matrix using setAA() and then multiplied to the current matrix.
Last night I thought what if I rotate the input vector of the AA by the current matrix and then create the temp matrix and multiple.
So it would look like this:
public void addRotate4(float angle, float ax, float ay, float az) {
GLMatrix tmp = new GLMatrix();
GLVector3 vec = new GLVector3();
vec.v[0] = ax;
vec.v[1] = ay;
vec.v[2] = az;
mult(vec); //multiple vector by current matrix
tmp.setAA(angle, vec.v[0], vec.v[1], vec.v[2]);
mult4x4(tmp);
}
And it works as expected! The addRotate4() function now rotates on the original axis'es.
I have a function in my program which rotates a point (x_p, y_p, z_p) around another point (x_m, y_m, z_m) by the angles w_nx and w_ny.
The new coordinates are stored in global variables x_n, y_n, and z_n. Rotation around the y-axis (so changing value of w_nx - so that the y - values are not harmed) is working correctly, but as soon as I do a rotation around the x- or z- axis (changing the value of w_ny) the coordinates aren't accurate any more. I commented on the line I think my fault is in, but I can't figure out what's wrong with that code.
void rotate(float x_m, float y_m, float z_m, float x_p, float y_p, float z_p, float w_nx ,float w_ny)
{
float z_b = z_p - z_m;
float x_b = x_p - x_m;
float y_b = y_p - y_m;
float length_ = sqrt((z_b*z_b)+(x_b*x_b)+(y_b*y_b));
float w_bx = asin(z_b/sqrt((x_b*x_b)+(z_b*z_b))) + w_nx;
float w_by = asin(x_b/sqrt((x_b*x_b)+(y_b*y_b))) + w_ny; //<- there must be that fault
x_n = cos(w_bx)*sin(w_by)*length_+x_m;
z_n = sin(w_bx)*sin(w_by)*length_+z_m;
y_n = cos(w_by)*length_+y_m;
}
What the code almost does:
compute difference vector
convert vector into spherical coordinates
add w_nx and wn_y to the inclination and azimuth angle (see link for terminology)
convert modified spherical coordinates back into Cartesian coordinates
There are two problems:
the conversion is not correct, the computation you do is for two inclination vectors (one along the x axis, the other along the y axis)
even if computation were correct, transformation in spherical coordinates is not the same as rotating around two axis
Therefore in this case using matrix and vector math will help:
b = p - m
b = RotationMatrixAroundX(wn_x) * b
b = RotationMatrixAroundY(wn_y) * b
n = m + b
basic rotation matrices.
Try to use vector math. Decide in which order you rotate, first along x, then along y perhaps.
If you rotate along z-axis, [z' = z]
x' = x*cos a - y*sin a;
y' = x*sin a + y*cos a;
The same repeated for y-axis: [y'' = y']
x'' = x'*cos b - z' * sin b;
z'' = x'*sin b + z' * cos b;
Again rotating along x-axis: [x''' = x'']
y''' = y'' * cos c - z'' * sin c
z''' = y'' * sin c + z'' * cos c
And finally the question of rotating around some specific "point":
First, subtract the point from the coordinates, then apply the rotations and finally add the point back to the result.
The problem, as far as I see, is a close relative to "gimbal lock". The angle w_ny can't be measured relative to the fixed xyz -coordinate system, but to the coordinate system that is rotated by applying the angle w_nx.
As kakTuZ observed, your code converts point to spherical coordinates. There's nothing inherently wrong with that -- with longitude and latitude, one can reach all the places on Earth. And if one doesn't care about tilting the Earth's equatorial plane relative to its trajectory around the Sun, it's ok with me.
The result of not rotating the next reference axis along the first w_ny is that two points that are 1 km a part of each other at the equator, move closer to each other at the poles and at the latitude of 90 degrees, they touch. Even though the apparent purpose is to keep them 1 km apart where ever they are rotated.
if you want to transform coordinate systems rather than only points you need 3 angles. But you are right - for transforming points 2 angles are enough. For details ask Wikipedia ...
But when you work with opengl you really should use opengl functions like glRotatef. These functions will be calculated on the GPU - not on the CPU as your function. The doc is here.
Like many others have said, you should use glRotatef to rotate it for rendering. For collision handling, you can obtain its world-space position by multiplying its position vector by the OpenGL ModelView matrix on top of the stack at the point of its rendering. Obtain that matrix with glGetFloatv, and then multiply it with either your own vector-matrix multiplication function, or use one of the many ones you can obtain easily online.
But, that would be a pain! Instead, look into using the GL feedback buffer. This buffer will simply store the points where the primitive would have been drawn instead of actually drawing the primitive, and then you can access them from there.
This is a good starting point.
I am trying to optimize the simulation function in my experiment so I can have more artificial brain-controlled agents running at a time. I profiled my code and found out that the big bottleneck in my code right now is computing the relative angle from every agent to every agent, which is O(n2), minus some small optimizations I have done. Here is the current code snippet I have for computing the angle:
[C++]
double calcAngle(double fromX, double fromY, double fromAngle, double toX, double toY)
{
double d = 0.0;
double Ux = 0.0, Uy = 0.0, Vx = 0.0, Vy = 0.0;
d = sqrt( calcDistanceSquared(fromX, fromY, toX, toY) );
Ux = (toX - fromX) / d;
Uy = (toY - fromY) / d;
Vx = cos(fromAngle * (cPI / 180.0));
Vy = sin(fromAngle * (cPI / 180.0));
return atan2(((Ux * Vy) - (Uy * Vx)), ((Ux * Vx) + (Uy * Vy))) * 180.0 / cPI;
}
I have two 2D points (x1, y1) and (x2, y2) and the facing of the "from" point (xa). I want to compute the angle that agent x needs to turn (relative to its current facing) to face agent y.
According to the profiler, the most expensive part is the atan2. I have Googled for hours and the above solution is the best solution I could find. Does anyone know of a more efficient way to compute the angle between two points? I am willing to sacrifice a little accuracy (+/- 1-2 degrees) for speed, if that affects anything.
As has been mentioned in the comments, there are probably high-level approaches to reduce your computational load.
But to the question in hand, you can just use the dot-product relationship:
theta = acos ( a . b / ||a|| ||b|| )
where a and b are your vectors, . denotes "dot product" and || || denotes "vector magnitude".
Essentially, this will replace your {sqrt, cos, sin, atan2} with {sqrt, acos}.
I would also suggest sticking to radians for all internal calculations, only converting to and from degrees for human-readable I/O.
Your comment tells a lot: "I am simulating a 180 degree frontal retina for every agent, so I need the angle". No, you don't. You just need to know whether the angle between the position vector and vision vector is more or less than 90 degrees.
That's very easy: the dot product A·B is >0 if the angle between A and B is less than 90 degrees; 0 if the angle is precisely 90 degrees, and <0 if the angle is more than 90 degrees. Calculating this takes 3 multiplications and 2 additions.
i think it's more a mathematical problem:
try
abs(arctan((y1-yfrom)/(x1-xfrom)) - arctan(1/((y2-yfrom2)/(x2-xfrom2))))
Use the dot product of these two vectors and at worst you need to do an inverse cosine instead:
A = Facing direction. B = Direction of Agent Y from Agent X
Calculating the dot is simple multiplication and addition. From that you have the cosine of the angle.
For starters, you should realize that there are a couple of simplifications that can reduce the calculations a bit:
You need not calculate the angle from an agent to itself,
If you have the angle from agent i to agent j, you already know something about the angle from agent j back to agent i.
I have to ask: what does "agent i turn to face agent j" mean? If the two surfaces are looking right at each other, do you have to do a calculation? What tolerance do you have on "looking right at each other"?
It'd be easier to recommend what to do if you'd stop focusing on the mathematics and describe the problem more fully.