I have a quaternion for an object's starting rotation, and a quaternion for an object's ending rotation, and I am SLERPing the shortest rotation between the two.
How can I figure out the magnitude of the rotation between the object's start and end rotations?
Let's introduce two quaternions qStart and qEnd. Magnitude of rotation between them can be expressed as a quaternion:
qRot = qEnd * qStart.inversed();
and the exact angle of rotation can be extracted as:
2*atan2(qRot.vec.length(), qRot.w);
Related
I'm trying to figure out, if I'm given a rotation in some form (matrix, quaternion, euler, rotvec, etc.) that's meant to be associated with an object that is known to have rotational symmetry along some axes of some order, how can I convert the given rotation so that the magnitude along those axes is always the lowest magnitude (e.g. if an object has rotation [0, 0, pi] with symmetry on the z-axis of order 4, I would get [0, 0, 0])?
The final rotation will be used to get the relative pose of the object to a camera, so I want to make sure to disambiguate the rotations.
I think that one approach I'm thinking of is to isolate the rotation along the symmetrical axis, reduce the magnitude, and then recompose the rotation.
R = R' * Rsymmetric
Rfinal = R' * Rsymmetric_lowest
I'm not certain how this would work if the object had multiple axes of symmetry.
While playing with quaternions, I noticed that I could not find the angle of rotation of a vector using the dot product between this vector, and its original position. In my example, I rotated a vector by 90 degrees around an arbitrary axis, but the dot product yielded a different angle.
// Axis of rotation (unit vector).
Vec3 Axis = Vec3(1, 1, 0) / sqrt(1 + 1 + 0);
// Creates a quaternion that will rotate a point by 90 degrees around the (1, 1, 0) axis.
Quat q(cos(3.14 / 4), Axis * sin(3.14 / 4));
// Creates a point.
Vec3 Point = Vec3(1, 0, 0);
// Rotates the point by q.
Quat Rot = q * Quat(0, Point) * q.GetConjugate();// Rot == (0, 0.5, 0.5, -0.707)
// Getting Rot's coordinates.
Vec3 v = Vec3(Rot.x, Rot.y, Rot.z);
// Angle is equal to 1.047, but it should be 1.57 (3.14/2)...
float Angle = acos(Dot(Point, v));
Note that every vector and quaternion is of length of 1.
I find that really intriguing, because the shortest angle between a vector rotated by 90 degrees and its original position is 90 degrees.
So my question is: why am I not getting 1.57 radians? What I am not understanding here?
Thank you for your attention.
With the dot product, you are measuring the angle between the initial vector, and the rotated vector, which is generally not the same as the magnitude of the applied rotation.
Imagine if your point was on the rotation axis, then the point would not move when the rotation is applied. To test this, set your point to (1, 1, 0)/sqrt(2), and the angle should be zero.
Then set the point to (1,-1,0)/sqrt(2) and you should get your expected pi/2.
Since you are rotation about the origin, only the component of the point perpendicular to the axis of rotation is effected by the rotation.
A good reference:
https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
Given the two following inputs:
a point on a sphere (like an observer on Earth);
and the world matrix of an object in space (the position and attitude of a satellite),
how to get the azimuth and elevation of the object in the tangent space of the point on the sphere (the elevation and azimuth of where the observer should look at)? In particular, when the object is exactly at the zenith, the yaw rotation (rotation around the vertical axis) should account for the azimuth (so that, though the observer is looking straight up, his shoulders would be facing the same azimuth as the object).
The math I've tried so far is:
to put the satellite in tangent space (multiplying its world matrix with the inverse of the matrix of the tangent space on the globe). Or the same with quaternions. An euler rotation is then deduced from the resulting matrix (or the resulting quaternion), with a "ZXY" priority, and the Z and X are interpreted as azimuth and elevation. But this gives incorrect numbers, as part of the rotation seems often interpreted as roll (Y axis rotation) which I want to be zero.
an intuitive approach also is to compute the angle between the vector of the observer to the object's position, with the vertical axis, to deduce the elevation; whereas the azimuth is given by the angle between the tangent north and the projected position of the object on the "tangent ground" (plus some more math to hone this particular deduction). But this approach does not work for the case of the object at the zenith.
Resources exist online but not with these specific inputs and the necessity of supporting the zenith case.
Incidentally the program is in typescript for three.js, and so the code goes as follows for the first solution described above:
function getRotationAtPoint(
object: THREE.Object3D,
point: THREE.Vector3
): { azimuth: number, elevation: number } {
// 1. Get the matrix of the tangent space of the observer.
const tangentSpaceMatrix = new THREE.Matrix4();
const baseTangentSpaceAxes = getBaseTangentAxesOnSphere(point);
tangentSpaceMatrix.makeBasis(...baseTangentSpaceAxes);
// 2. Tranform the object's matrix in tangent space of observer.
const inverseMatrix = new THREE.Matrix4().getInverse(tangentSpaceMatrix);
const objectMatrix = object.matrixWorld.clone().multiply(inverseMatrix);
// 3. Get the angles.
const euler = new THREE.Euler().setFromRotationMatrix(objectMatrix);
return {
azimuth: euler.z,
elevation: euler.x
};
}
Also, Three.js offers references to the up axis of THREE.Object3D instances, however the program I deal with computes everything directly into the objects' matrices and the up axis can't be trusted.
I am extracting in OpenGL the Model Matrix with
glGetFloatv (GL_MODELVIEW_MATRIX, (float*)x)
And would like to extract from the resulting 4x4 matrix the x,y and z axis rotations. How Can I do that ?
Thanks !
First you should know, that x,y,z axis rotations, called Euler Angles suffer from serious numerical problems. Also they're not unambigous. So either you store a rotation angle and the rotation axis, thus effectively forming a quaternion in disguise, or you stick with the full rotation matrix.
Find the quaternion from a rotation matrix is called an eigenvalue problem. Technically you're determining the eigenvector of the rotation matrix, which is the axis and the magnitude designates the angle.
I'm writing a CAD-like app, so I understand your problem, we 'in the business' know how awful Euler angles are for linear transformations - but the end-user finds them far more intuitive than matrices or quaternions.
For my app I interpreted Ken Shoemake's wonderful algorithm, it's one of the very few that support arbitrary rotation orders. It's from '93, so it's in pure C code - not for the faint hearted!
http://tog.acm.org/resources/GraphicsGems/gemsiv/euler_angle/
Something like this should give you what you're after.
final double roll = Math.atan2(2 * (quat.getW() * quat.getX() + quat.getY() * quat.getZ()),
1 - 2 * (quat.getX() * quat.getX() + quat.getY() * quat.getY()));
final double pitch = Math.asin(2 * (quat.getW() * quat.getY() - quat.getZ() * quat.getY()));
final double yaw = Math.atan2(2 * (quat.getW() * quat.getZ() + quat.getX() * quat.getY()), 1 - 2 * (quat.getY()
* quat.getY() + quat.getZ() * quat.getZ()));
I use this as a utility function to print out camera angles when I'm using SLERP to interpolate between 2 quaternions that I've derived from 2 4x4 matrices (i.e. camera movement between 2 3D points).
I'm doing a scene using openGL (a house). I want to do some collision detection, mainly with the walls in the house.
I have tried the following code:
// a plane is represented with a normal and a position in space
Vector planeNor(0,0,1);
Vector position(0,0,-10);
Plane p(planeNor,position);
Vector vel(0,0,-1);
double lamda; // this is the intersection point
Vector pNormal; // the normal of the intersection
// this method is from Nehe's Lesson 30
coll= p.TestIntersionPlane(vel,Z,lamda,pNormal);
glPushMatrix();
glBegin(GL_QUADS);
if(coll)
glColor3f(1,0,0);
else
glColor3f(1,1,1);
glVertex3d(0,0,-10);
glVertex3d(3,0,-10);
glVertex3d(3,3,-10);
glVertex3d(0,3,-10);
glEnd();
glPopMatrix();
Nehe's method:
#define EPSILON 1.0e-8
#define ZERO EPSILON
bool Plane::TestIntersionPlane(const Vector3 & position,const Vector3 & direction, double& lamda, Vector3 & pNormal)
{
double DotProduct=direction.scalarProduct(normal); // Dot Product Between Plane Normal And Ray Direction
double l2;
// Determine If Ray Parallel To Plane
if ((DotProduct<ZERO)&&(DotProduct>-ZERO))
return false;
l2=(normal.scalarProduct(position))/DotProduct; // Find Distance To Collision Point
if (l2<-ZERO) // Test If Collision Behind Start
return false;
pNormal= normal;
lamda=l2;
return true;
}
Z is initially (0,0,0) and every time I move the camera towards the plane, I reduce its z component by 0.1 (i.e. Z.z-=0.1 ).
I know that the problem is with the vel vector, but I can't figure out what the right value should be. Can anyone please help me?
You're passing "vel" (which I suppose is velocity of the moving thing) as "Position", and Z (which I suppose is position) as "Direction".
Your calculation of "Distance to Collision Point" makes no sense. It doesn't take position of the plane into account at all (or maybe it does, if the variables are misnamed).
You define pNormal, but I can't see any use for it. Is it supposed to mean something else?
It's almost impossible to get something like this working without understanding the math. Try a simpler version of the test, maybe assuming a z=0 plane and +z-axis movement, get that working and then take another look at the general case.
Thank you for your help.
I looked into the code again and I changed the collision detection method into the following:
//startPoint: the ray's starting point.
//EndPoint: the ray's ending point.
//lamda: the intersection point.
bool Plane::TestIntersionPlane(const Vector3& startPoint,const Vector3& Endpoint, double& lamda)
{
double cosAlpha=Endpoint.scalarProduct(normal); // calculates the angle between the plane's normal and the ray vector.
// Determine If Ray Parallel To Plane
if ((cosAlpha<ZERO)&&(cosAlpha>-ZERO))
return false;
// delta D is the plane's distance from the origin minus the ray's distance from the origin.
double deltaD = distance - startPoint.scalarProduct(normal); //distance is a double representing the plane's distance from the origin.
lamda= deltaD/cosAlpha;// distance between the plane and the vector
// if the distance between the ray and the plane is greater than zero then they haven't intersected.
if(lamda > ZERO)
return false;
return true;
}
This seems to work with all planes except when the ray is too far from the plane. For example if the plane is at z=-10 and the ray's starting point is: 0,0,3 and it's ending point is 0,0,2 then this is detected as a collision but when I move the ray to start(0,0,2) and end(0,0,1) it's not detected as a collision.
The math seems correct to me, so I'm not sure how to handle this.