Direct3D - How do I calculate Roll from View Matrix? - c++

This one has been eating up my entire night, and I'm finally throwing up my hands for some assistance. Basically, it's fairly straightforward to calculate the Pitch and Yaw from the View Matrix right after you do a camera update:
D3DXMatrixLookAtLH(&m_View, &sCam.pos, &vLookAt, &sCam.up);
pDev->SetTransform(D3DTS_VIEW, &m_View);
// Set the camera axes from the view matrix
sCam.right.x = m_View._11;
sCam.right.y = m_View._21;
sCam.right.z = m_View._31;
sCam.up.x = m_View._12;
sCam.up.y = m_View._22;
sCam.up.z = m_View._32;
sCam.look.x = m_View._13;
sCam.look.y = m_View._23;
sCam.look.z = m_View._33;
// Calculate yaw and pitch and roll
float lookLengthOnXZ = sqrtf( sCam.look.z^2 + sCam.look.x^2 );
fPitch = atan2f( sCam.look.y, lookLengthOnXZ );
fYaw = atan2f( sCam.look.x, sCam.look.z );
So my problem is: there must be some way to obtain the Roll in radians similar to how the Pitch and Yaw are being obtained at the end of the code there. I've tried several dozen algorithms that seemed to make sense to me, but none of them gave quite the desired result. I realize that many developers don't track these values, however I am writing some re-centering routines and setting clipping based on the values, and manual tracking breaks down as you apply mixed rotations to the View Matrix. So, does anyone have the formula for getting the Roll in radians (or degrees, I don't care) from the View Matrix?

Woohoo, got it! Thank you stacker for the link to the Euler Angles entry on Wikipedia, the gamma formula at the end was indeed the correct solution! So, to calculate Roll in radians from a "vertical plane" I'm using the following line of C++ code:
fRoll = atan2f( sCam.up.y, sCam.right.y ) - D3DX_PI/2;
As Euler pointed out, you're looking for the arc tangent of the Y (up) matrix's Z value against the X (right) matrix's Z value - though in this case I tried the Z values and they did not yield the desired result so I tried the Y values on a whim and I got a value which was off by +PI/2 radians (right angle to the desired value), which is easily compensated for. Finally I have a gryscopically accurate and stable 3D camera platform with self-correcting balance. =)

It seems the Wikipedia article about Euler angles contains the formula you're lookin for at the end.

Related

OpenGL : Rotate using roll pitch and yaw values

I have an issue of rotating my model. I have file that are composed of relative interger values for roll, pitch and yaw.
Thus every period of time I read those values and applie to my model :
rotate(current_yaw - previous_yaw, QVector3D(0,0,1)) ;
rotate(current_pitch - previous_pitch, QVector3D(0, 1, 0)) ;
rotate(current_roll - previous_roll, QVector3D(1,0,0)) ;
The rotate function multiplies the transformation/model matrix with a rotation matrix.
My issue is that when the values go back to 0, the model is not straight.
I tried doing one rotation only (only the yaw, then the roll, then the pitch) and it works fine. So the issue is with combining the rotations.
I know that order matters. I would like to do, for instance, roll - pitch - yaw and afterwards yaw - pitch -roll to get back to the initial position of the model.
But in case of thousands of non symetric values, where some angles may be positives and others negatives, I don't really know what to do.
Is there a way to apply all those rotation at the same time ?
Edit :
To try and apply all the rotations at the same time I tried using QQuaternion. Here is an example of what I though would work but still does not.
QQuaternion rotation = QQuaternion::fromEulerAngles(20, 20, 20) ;
m_world.setToIdentity();
m_world.rotate(rotation);
QQuaternion rotation_back = QQuaternion::fromEulerAngles(-20, -20, -20) ;
m_world.setToIdentity();
m_world.rotate(rotation_back);
The world matrix is not identity in the end.
Edit : of course the second quaternion should be (0,0,0)... I need to use angles not differences of angles. Works fine.

2d rotation on set of points causes skewing distortion

I'm writing an application in OpenGL (though I don't think this problem is related to that). I have some 2d point set data that I need to rotate. It later gets projected into 3d.
I apply my rotation using this formula:
x' = x cos f - y sin f
y' = y cos f + x sin f
Where 'f' is the angle. When I rotate the point set, the result is skewed. The severity of the effect varies with the angle.
It's hard to describe so I have pictures;
The red things are some simple geometry. The 2d point sets are the vertices for the white polylines you see around them. The first picture shows the undistorted pointsets, and the second picture shows them after rotation. It's not just skew that's occuring with the rotation; sometimes it seems like displacement occurs as well.
The code itself is trivial:
double cosTheta = cos(2.4);
double sinTheta = sin(2.4);
CalcSimplePolyCentroid(listHullVx,xlate);
for(size_t j=0; j < listHullVx.size(); j++) {
// translate
listHullVx[j] = listHullVx[j] - xlate;
// rotate
double xPrev = listHullVx[j].x;
double yPrev = listHullVx[j].y;
listHullVx[j].x = ((xPrev*cosTheta) - (yPrev*sinTheta));
listHullVx[j].y = ((yPrev*cosTheta) + (xPrev*sinTheta));
// translate
listHullVx[j] = listHullVx[j] + xlate;
}
If I comment out the code under '//rotate' above, the output of the application is the first image. And adding it back in gives the second image. There's literally nothing else that's going on (afaik).
The data types being used are all doubles so I don't think its a precision issue. Does anyone have any idea why rotation would cause skewing like the above pictures show?
EDIT
filipe's comment below was correct. This probably has nothing to do with the rotation and I hadn't provided enough information for the problem;
The geometry I've shown in the pictures represents buildings. They're generated from lon/lat map coordinates. In the point data I use to do the transform, I forgot to use an actual projection to cartesian coordinate space and just mapped x->lon, y->lat, and I think this is the reason I'm seeing the distortion. I'm going to request that this question be deleted since I don't think it'll be useful to anyone else.
Update:
As a result of your comments it tunred out the it is unlikely that the bug is in the presented code.
One final other hint: std transform formulars are only valid if the cooridnate system is cartesian,
on ios you sometimes have inverted y Achsis.

Quaternion - Rotate To

I have some object in world space, let's say at (0,0,0) and want to rotate it to face (10,10,10).
How do i do this using quaternions?
This question doesn't quite make sense. You said that you want an object to "face" a specific point, but that doesn't give enough information.
First, what does it mean to face that direction? In OpenGL, it means that the -z axis in the local reference frame is aligned with the specified direction in some external reference frame. In order to make this alignment happen, we need to know what direction the relevant axis of the object is currently "facing".
However, that still doesn't define a unique transformation. Even if you know what direction to make the -z axis point, the object is still free to spin around that axis. This is why the function gluLookAt() requires that you provide an 'at' direction and an 'up' direction.
The next thing that we need to know is what format does the end-result need to be in? The orientation of an object is often stored in quaternion format. However, if you want to graphically rotate the object, then you might need a rotation matrix.
So let's make a few assumptions. I'll assume that your object is centered at the world's point c and has the default alignment. I.e., the object's x, y, and z axes are aligned with the world's x, y, and z axes. This means that the orientation of the object, relative to the world, can be represented as the identity matrix, or the identity quaternion: [1 0 0 0] (using the quaternion convention where w comes first).
If you want the shortest rotation that will align the object's -z axis with point p:=[p.x p.y p.z], then you will rotate by φ around axis a. Now we'll find those values. First we find axis a by normalizing the vector p-c and then taking the cross-product with the unit-length -z vector and then normalizing again:
a = normalize( crossProduct(-z, normalize(p-c) ) );
The shortest angle between those two unit vectors found by taking the inverse cosine of their dot-product:
φ = acos( dotProduct(-z, normalize(p-c) ));
Unfortunately, this is a measure of the absolute value of the angle formed by the two vectors. We need to figure out if it's positive or negative when rotating around a. There must be a more elegant way, but the first way that comes to mind is to find a third axis, perpendicular to both a and -z and then take the sign from its dot-product with our target axis. Vis:
b = crossProduct(a, -z );
if ( dotProduct(b, normalize(p-c) )<0 ) φ = -φ;
Once we have our axis and angle, turning it into a quaternion is easy:
q = [cos(φ/2) sin(φ/2)a];
This new quaternion represents the new orientation of the object. It can be converted into a matrix for rendering purposes, or you can use it to directly rotate the object's vertices, if desired, using the rules of quaternion multiplication.
An example of calculating the Quaternion that represents the rotation between two vectors can be found in the OGRE source code for the Ogre::Vector3 class.
In response to your clarification and to just answer this, I've shamelessly copied a very interesting and neat algorithm for finding the quat between two vectors that looks like I have never seen before from here. Mathematically, it seems valid, and since your question is about the mathematics behind it, I'm sure you'll be able to convert this pseudocode into C++.
quaternion q;
vector3 c = cross(v1,v2);
q.v = c;
if ( vectors are known to be unit length ) {
q.w = 1 + dot(v1,v2);
} else {
q.w = sqrt(v1.length_squared() * v2.length_squared()) + dot(v1,v2);
}
q.normalize();
return q;
Let me know if you need help clarifying any bits of that pseudocode. Should be straightforward though.
dot(a,b) = a1*b1 + a2*b2 + ... + an*bn
and
cross(a,b) = well, the cross product. it's annoying to type out and
can be found anywhere.
You may want to use SLERP (Spherical Linear Interpolation). See this article for reference on how to do it in c++

Confused about degrees and OpenGL/GLUT camera movement/rotation

NOTICE: I have edited the question below which is more relevant to my real issue than the text right below, you can skip this if you but I'll leave it here for historic reasons.
To see if I get this right, a float in C is the same as a value in radians right? I mean, 360º = 6.28318531 radians and I just noticed on my OpenGL app that a full rotation goes from 0.0 to 6.28, which seems to add up correctly. I just want to make sure I got that right.
I'm using a float (let's call it anglePitch) from 0.0 to 360.0 (it's easier to read in degrees and avoids casting int to float all the time) and all the code I see on the web uses some kind of DEG2RAD() macro which is defined as DEG2RAD 3.141593f / 180. In the end it would be something like this:
anglePitch += direction * 1; // direction will be 1 or -1
refY = tan(anglePitch * DEG2RAD);
This really does a full rotation but that full rotation will be when anglePitch = 180 and anglePitch * DEG2RAD = 3.14, but a full rotation should be 360|6.28. If I change the macro to any of the following:
#define DEG2RAD 3.141593f / 360
#define DEG2RAD 3.141593f / 2 / 180
It works as expected, a full rotation will happen when anglePitch = 360.
What am I missing here and what should I use to properly convert angles to radians/floats?
IMPORTANT EDIT (REAL QUESTION):
I understand now the code I see everywhere on the web about DEG2RAD, I'm just too dumb at math (yeah, I know, it's important when working with this kind of stuff). So I'm going to rephrase my question:
I have now added this to my code:
#define PI 3.141592654f
#define DEG2RAD(d) (d * PI / 180)
Now, when working the pitch/yawn angles in degrees, which are floats, once again, to avoid casting all the time, I just use the DEG2RAD macro and the degree value will be correctly converted to radians. These values will be passed to sin/cos/tan functions and will return the proper values to be used in GLUT camera.
Now the real question, where I was really confused before but couldn't explain myself better:
angleYaw += direction * ROTATE_SPEED;
refX = sin(DEG2RAD(angleYaw));
refZ = -cos(DEG2RAD(angleYaw));
This code will be executed when I press the LEFT/RIGHT keys and the camera will rotate in the Y axis accordingly. A full rotation goes from 0º to 360º.
anglePitch += direction * ROTATE_SPEED;
refY = tan(DEG2RAD(anglePitch));
This is similar code and will be executed when I press the UP/DOWN keys and the camera will rotate in the X axis. But in this situation, a full rotation goes from 0º to 180º degrees and that's what's really confusing me. I'm sure it has something to do with the tangent function but I can't get my head around it.
Is there way I could use sin/cos (as I do in the yawn code) to achieve the same rotation? What is the right way, the most simple code I can add/fix and what makes more sense to create a full pitch rotation from 0º to 360º?
360° = 2 * Pi, Pi = 3.141593…
Radians are defined by the arc length of an angle along a circle of radius 1. The circumfence of a circle is 2*r*Pi, so one full turn on a unit circle has an arc length of 2*Pi = 6.28…
The measure of angles in degrees stem from the fact, that by aligning 6 equilateral triangles you span a full turn. So we have 6 triangles, each making up a 6th of the turn, so the old babylonians divided a circle into pieces of 1/(6*6) = 1/36, and to further refine it this was subdivded by 10. That's why we ended up with 360° in a full circle. This number is arbitrarily choosen, though.
So if there are 2*Pi/360° this makes Pi/180° = 3.141593…/180° which is the conversion factor from degrees to radians. The reciprocal, 180°/Pi = 180/3.141593…
Why on earth the old OpenGL function glRotate and GLU's gluPerspective used degrees instead of radians I cannot fathom. From a mathematical point of view only radians make sense. Which I think is most beautifully demonstrated by Euler's equation
e^(i*Pi) - 1 = 0
There you have it, all the important numbers of mathematics in one single equation. What's this got to do with angles? Well:
e^(i*alpha) = cos(alpha) + i * sin(alpha), alpha is in radians!
EDIT, with respect to modified question:
Your angles being floats is all fine. Why would you even think degress being integers I cannot understand. Normally you don't have to define PI yourself, it comes predefined in math.h, usually called M_PI, M_2PI, and M_PI2 for Pi, 2*Pi and Pi/2. You also should change your macro, the way it's written now can create strange effects.
#define DEG2RAD(d) ( (d) * M_PI/180. )
GLUT has no camera at all. GLUT is a rather dumb OpenGL framework I recommend not using. You probably refer to gluLookAt.
Those obstacles out of the way let's see what you're doing there. Remember that trigonometric functions operate on the unit circle. Let the angle 0 point towards the right and angles increment counterclockwise. Then sin(a) is defined as the amount of rightwards and cos(a) and the amount of forwards to reach the point at angle a on the unit circle. This is what the refX and refZ are getting assigned to.
refY however makes no sense written that way. tan = sin/cos so as we approach n*pi/2 (i.e. 90°) it diverges to +/- infinity. At least it explains your pi/180° cyclic range, because that's the period of tan.
I was first thinking that tan may have been used to normalize the direction vector, but didn't make sense either. The factor would have been 1./sqrt(sin²(Pitch) + 1)
I double checked: using tan there does the right thing.
EDIT2: I don't see where your problem is: The pitch angle is -90° to +90°, which makes perfect sense. Go get yourself a globe (of the earth): The east-west coordinates (longitude) go from -180° to +180°, the south-north coordinate (latitude) goes -90° to +90°. Think about it: Any larger coordinate range would create ambiguities.
The only good suggestion I offer you is: Grab some math text book and bend your mind around spherical coordinates! Sorry to tell you that way. Whatever you have works perfectly fine, you just need to understand sperical geometry.
You're using the terms Yaw and Pitch. Those are normally used in Euler angles. Now unfortunately Euler angles, which compelling at first, cause serious trouble later on (like gimbal lock). You should not use them at all. It may also be a good idea if you used some pencil/sticks/whatever to decompose the rotations you're intending with your hands to understand their mechanics.
And by the way: There are also non-integer degrees. Just hop over to http://maps.google.com to see them in action (just select some place and let http://maps.google.com give you the link to it).
'float' is a type, like int or double. radians and degrees are units of measure, both of which can be represented with any precision you want. i.e., there's no reason you can't have 22.5 degrees, and keep that value in a float.
a full rotation in radians is 2*pi, about 6.283, whereas a full rotation in degrees is 360. You can convert between them by dividing out the starting unit's full circle, then multiplying by the desired unit's full circle.
for example, to get from 90 degrees to radians, first divide out the degrees. 90 over 360 is 0.25 (note this value is in 'revolutions'). Now multiply that 0.25 by 6.283 to arrive at 1.571 radians.
follow up
the reason you're seeing your pitch cycle twice as fast as it should is precisely because you're using tan(pitch) to compute the Y component. What you should have is that the Y component depends on sin(pitch). i.e., try changing
refY = tan(DEG2RAD(anglePitch));
to
refY = sin(DEG2RAD(anglePitch));
a technical detail: the numbers that go into the look matrix should all be in the range of -1 to +1, and if you were to inspect the values you're feeding to refY, and run your pitch outside of -45 to +45 degrees, you'd see the problem; tan() runs off to infinity at +/-90 degrees.
also, note that casting a value from int to float in no sense converts between degrees and radians. casting just gives you the nearest equivalent value in the new storage type. for example, if you cast the integer 22 to floating point, you get 22.0f, whereas if you cast 33.3333f to type int, you'd be left with 33. when working with angles, you really should just stick with floating point, unless you're constrained by working with an embedded processor or something. this is especially important with radians, where whole number increments represent leaps of (about) 57.3 degrees.
Assuming that your ref components are intended to be used as your look-at vector, I think what you need is
refY = sin(DEG2RAD(anglePitch));
XZfactor = cos(DEG2RAD(anglePitch));
refX = XZfactor*sin(DEG2RAD(angleYaw));
refZ = -XZfactor*cos(DEG2RAD(angleYaw));

Optimizing a pinhole camera rendering system

I'm making a software rasterizer for school, and I'm using an unusual rendering method instead of traditional matrix calculations. It's based on a pinhole camera. I have a few points in 3D space, and I convert them to 2D screen coordinates by taking the distance between it and the camera and normalizing it
Vec3 ray_to_camera = (a_Point - plane_pos).Normalize();
This gives me a directional vector towards the camera. I then turn that direction into a ray by placing the ray's origin on the camera and performing a ray-plane intersection with a plane slightly behind the camera.
Vec3 plane_pos = m_Position + (m_Direction * m_ScreenDistance);
float dot = ray_to_camera.GetDotProduct(m_Direction);
if (dot < 0)
{
float time = (-m_ScreenDistance - plane_pos.GetDotProduct(m_Direction)) / dot;
// if time is smaller than 0 the ray is either parallel to the plane or misses it
if (time >= 0)
{
// retrieving the actual intersection point
a_Point -= (m_Direction * ((a_Point - plane_pos).GetDotProduct(m_Direction)));
// subtracting the plane origin from the intersection point
// puts the point at world origin (0, 0, 0)
Vec3 sub = a_Point - plane_pos;
// the axes are calculated by saying the directional vector of the camera
// is the new z axis
projected.x = sub.GetDotProduct(m_Axis[0]);
projected.y = sub.GetDotProduct(m_Axis[1]);
}
}
This works wonderful, but I'm wondering: can the algorithm be made any faster? Right now, for every triangle in the scene, I have to calculate three normals.
float length = 1 / sqrtf(GetSquaredLength());
x *= length;
y *= length;
z *= length;
Even with a fast reciprocal square root approximation (1 / sqrt(x)) that's going to be very demanding.
My questions are thus:
Is there a good way to approximate the three normals?
What is this rendering technique called?
Can the three vertex points be approximated using the normal of the centroid? ((v0 + v1 + v2) / 3)
Thanks in advance.
P.S. "You will build a fully functional software rasterizer in the next seven weeks with the help of an expert in this field. Begin." I ADORE my education. :)
EDIT:
Vec2 projected;
// the plane is behind the camera
Vec3 plane_pos = m_Position + (m_Direction * m_ScreenDistance);
float scale = m_ScreenDistance / (m_Position - plane_pos).GetSquaredLength();
// times -100 because of the squared length instead of the length
// (which would involve a squared root)
projected.x = a_Point.GetDotProduct(m_Axis[0]).x * scale * -100;
projected.y = a_Point.GetDotProduct(m_Axis[1]).y * scale * -100;
return projected;
This returns the correct results, however the model is now independent of the camera position. :(
It's a lot shorter and faster though!
This is called a ray-tracer - a rather typical assignment for a first computer graphics course* - and you can find a lot of interesting implementation details on the classic Foley/Van Damm textbook (Computer Graphics Principes and Practice). I strongly suggest you buy/borrow this textbook and read it carefully.
*Just wait until you get started on reflections and refraction... Now the fun begins!
It is difficult to understand exactly what your code doing, because it seems to be performing a lot of redundant operations! However, if I understand what you say you're trying to do, you are:
finding the vector from the pinhole to the point
normalizing it
projecting backwards along the normalized vector to an "image plane" (behind the pinhole, natch!)
finding the vector to this point from a central point on the image plane
doing dot products on the result with "axis" vectors to find the x and y screen coordinates
If the above description represents your intentions, then the normalization should be redundant -- you shouldn't have to do it at all! If removing the normalization gives you bad results, you are probably doing something slightly different from your stated plan... in other words, it seems likely that you have confused yourself along with me, and that the normalization step is "fixing" it to the extent that it looks good enough in your test cases, even though it probably still isn't doing quite what you want it to.
The overall problem, I think, is that your code is massively overengineered: you are writing all your high-level vector algebra as code to be executed in the inner loop. The way to optimize this is to work out all your vector algebra on paper, find the simplest expression possible for your inner loop, and precompute all the necessary constants for this at camera setup time. The pinhole camera specs would only be the inputs to the camera setup routine.
Unfortunately, unless I miss my guess, this should reduce your pinhole camera to the traditional, boring old matrix calculations. (ray tracing does make it easy to do cool nonstandard camera stuff -- but what you describe should end up perfectly standard...)
Your code is a little unclear to me (plane_pos?), but it does seem that you could cut out some unnecessary calculation.
Instead of normalizing the ray (scaling it to length 1), why not scale it so that the z component is equal to the distance from the camera to the plane-- in fact, scale x and y by this factor, you don't need z.
float scale = distance_to_plane/z;
x *= scale;
y *= scale;
This will give the x and y coordinates on the plane, no sqrt(), no dot products.
Well, off the bat, you can calculate normals for every triangle when your program starts up. Then when you're actually running, you just have to access the normals. This sort of startup calculation to save costs later tends to happen a lot in graphics. This is why we have large loading screens in a lot of our video games!