How does one convert world coordinates to camera coordinates? - math

I have an input 3D vector, along with the pitch and yaw of the camera. Can anyone describe or provide a link to a resource that will help me understand and implement the required transformation and matrix mapping?

The world-to-camera transformation matrix is the inverse of the camera-to-world matrix. The camera-to-world matrix is the combination of a translation to the camera's position and a rotation to the camera's orientation. Thus, if M is the 3x3 rotation matrix corresponding to the camera's orientation and t is the camera's position, then the 4x4 camera-to-world matrix is:
M00 M01 M02 tx
M10 M11 M12 ty
M20 M21 M22 tz
0 0 0 1
Note that I've assumed that vectors are column vectors which are multiplied on the right to perform transformations. If you use the opposite convention, make sure to transpose the matrix.
To find M, you can use one of the formulas listed on Wikipedia, depending on your particular convention for roll, pitch, and yaw. Keep in mind that those formulas use the convention that vectors are row vectors which are multiplied on the left.
Instead of computing the camera-to-world matrix and inverting it, a more efficient (and numerically stable) alternative is to calculate the world-to-camera matrix directly. To do so, just invert the camera's position (by negating all 3 coordinates) and its orientation (by negating the roll, pitch, and yaw angles, and adjusting them to be in their proper ranges), and then compute the matrix using the same algorithm.

If we have a structure like this to describe a 4x4 matrix:
class Matrix4x4
{
public:
union
{
struct
{
Type Xx, Xy, Xz, Xw;
Type Yx, Yy, Yz, Yw;
Type Zx, Zy, Zz, Zw;
Type Wx, Wy, Wz, Ww;
};
struct
{
Vector3<Type> Right;
Type XW;
Vector3<Type> Up;
Type YW;
Vector3<Type> Look;
Type ZW;
Vector3<Type> Pos;
Type WW;
};
Type asDoubleArray[4][4];
Type asArray[16];
};
};
If all you have is Euler angles, that is an angles representing the yaw, pitch, and roll and a point in 3d space for the position, you can calculate the Right, Up, and Look vectors. Note that Right, Up, and Look are just the X,Y,Z Vectors, but since this is a camera, I find it easier to name it so. The simplest way to apply your roations to the camera matrix is to build a series of rotation matrices and multiply our camera matrix by each rotation matrix.
A good reference for that is here: http://www.euclideanspace.com
Once you have applied all the needed rotations, you can set the vector Pos to the camera's position in the world space.
Lastly, before you apply the camera's transformation, you need to take the camera's inverse of its matrix. This is what you are going to multiply your modelview matrix by before you start drawing polygons. For the matrix class above, the inverse is calculated like this:
template <typename Type>
Matrix4x4<Type> Matrix4x4<Type>::OrthoNormalInverse(void)
{
Matrix4x4<Type> OrthInv;
OrthInv = Transpose();
OrthInv.Xw = 0;
OrthInv.Yw = 0;
OrthInv.Zw = 0;
OrthInv.Wx = -(Right*Pos);
OrthInv.Wy = -(Up*Pos);
OrthInv.Wz = -(Look*Pos);
return OrthInv;
}
So finally, with all our matrix constuction out of the way, you would be doing something like this:
Matrix4x4<float> cameraMatrix, rollRotation, pitchRotation, yawRotation;
Vector4<float> cameraPosition;
cameraMatrix = cameraMatrix * rollRotation * pitchRotation * yawRotation;
Matrix4x4<float> invCameraMat;
invCameraMat = cameraMatrix.OrthoNormalInverse();
glMultMatrixf(invCameraMat.asArray);
Hope this helps.

What you are describing is called 'Perspective Projection' and there are reams of resources on the web that explain the matrix math and give the code necessary to do this. You could start with the wikipedia page

Related

How do I find the angle between two 3D vectors for camera orientation?

I'm currently trying to finish my camera orientation and have ran into a problem where I will need to compute the angle between two vectors in order to rotate my camera to look in my desired direction. Currently my camera always looks at 0,0,0 regardless of whether I specify a camera 'lookat'. I have found out that my camera will only rotate by increasing/decreasing the floats that I have stored (which is used when creating the rotation matrix).
I am trying to rotate around the Y axis only and for rotation I am using XMMatrixRotationAxisY(vector, #);
I have VectorA and VectorB.
VectorA = the current lookat position of the camera and VectorB = the desired lookat position of the camera
How do I compute the angle to pass into XMVectorRotationY, based on the two vectors above?
XMFLOAT3 currentDirection = XMFLOAT3(1.0f, 0.0f, 1.0f);
XMFLOAT3 destinationDirection = XMFLOAT3(200.0f, 0.0f, 200.0f);
rotationY = ?
XMMatrixRotationAxisY(vector, rotationY);
? being the angle we wish to rotate by
The cross product of two vectors v1, v2, produces another vector (v) perpendicular to the plane defined by the two other vectors. This (v) is the axis of your rotation.
The angle for rotation is arccosine of the dot product of vectors v1, v2.
You can compute the decomposition of (v) projecting into the plane needed, which can be easy with the dot product again.
First, project these two vectors on XOZ (
v_proj = {dot(v,X),0,dot(v,Z)} where X,Y,Z are basis vectors and dot is dot product). Since you get current and destination vectors projected on XOZ,. normalize projections. Then find cos(theta) = dot(cur_proj, dest_proj). Call acos for angle. Or you can construct rotation matrix by yourself.
Rot_y =
cos(theta); 0; sin(theta);
0; 1; 0;
-sin(theta); 0; cos(theta)
where sin(theta) = |cur_proj X dest_proj| - cross product. To get the sign you need to look at Y component of cross vector

Scale vector along plane

I'm trying to apply friction to a 3D collision. The information I have is:
The velocity of the collision
The surface normal of the collider
An arbitrary friction coefficient (0 - 1 inclusive)
What I would like to do is multiply the portion of the velocity that is parallel to the plane by the friction coefficient, while leaving the portion parallel to the normal intact.
How can I go about performing this operation?
I was thinking perhaps this will involve the use of the dot-product, but then I started reading about matrices, then vector projection, and now I'm pretty lost.
I was able to solve the problem by doing the following:
Get a tangent vector for the normal
Use the normal and the tangent vector to get a rotation matrix for the surface
Use the inverse of the rotation matrix to transform the velocity vector
Scale the x and z components of the transformed vector by the friction coefficient
Use the rotation matrix to transform the velocity back again
I doubt this is the most efficient way of doing it, but it seems to have worked.
If you can do vector addition, scalar multiplication (i.e. multiplying a vector by a number) and the dot product, then this is all you need:
Vin = (V•Vnormal)Vnormal
Vpar = V - Vin
Vpar = kVpar (where k is the coefficient, and "=" means assignment)
Vin = -Vin
V = Vin + Vpar

Rotate quaternion on all 3 axis from axis angle in GLM

I use quaternions for rotations in OpenGL engine.Currently , in order to create rotation matrix for x ,y and z rotations I create a quaternion per axis rotation.Then I multiply these to get the final quaternion:
void RotateTo3(const float xr ,const float yr ,const float zr){
quat qRotX=angleAxis(xr, X_AXIS);
quat qRotY=angleAxis(yr, Y_AXIS);
quat qRotZ=angleAxis(zr, Z_AXIS);
quat resQuat=normalize(qRotX * qRotY * qRotZ);
resQuat=normalize(resQuat);
_rotMatrix= mat4_cast(resQuat);
}
Now it's all good but I want to create only one quaternion from all 3 axis angles and skip the final multiplication.One of the quat constructors has params for euler angles vector which goes like this:
quat resQuat(vec3(yr,xr,zr))
So if I try this the final rotation is wrong.(Also tried quat(vec3(xr,yr,zr)) ) .Isn't there a way in GLM to fill the final quaternion from all 3 axis in one instance ?
Now , one more thing:
As Nicol Bolas suggested , I could use glm::eulerAngleYXZ() to fill a rotation matrix right away as by his opinion it is pointless to do the intermediate quaternion step.. But what I found is that the function doesn't work properly , at least for me .For example :
This :
mat4 ex= eulerAngleX(radians(xr));
mat4 ey= eulerAngleY(radians(yr));
mat4 ez= eulerAngleZ(radians(zr));
rotMatrix= ex * ey * ez;
Doesn't return the same as this :
rotMatrix= eulerAngleYXZ(radians(yr),radians(xr),radians(zr));
And from my comparisons to the correct rotation state ,the first way gives the correct rotations while the second wrong .
Contrary to popular belief, quaternions are not magical "solve the Gimbal lock" devices, such that any uses of quaternions make Euler angles somehow not Euler angles.
Your RotateTo3 function takes 3 Euler angles and converts them into a rotation matrix. It doesn't matter how you perform this process; whether you use 3 matrices, 3 quaternions or glm::eulerAngleYXZ. The result will still be a matrix composed from 3 axial rotations. It will have all of the properties and failings of Euler angles. Because it is Euler angles.
Using quaternions as intermediaries here is pointless. It gains you nothing; you may as well just use matrices built from successive glm::rotate calls.
If you want to do orientation without Gimbal lock or the other Euler angle problems, then you need to stop representing your orientation as Euler angles.
In answer to the question you actually asked, you can use glm::eulerAngleYXZ to compute
Do you mean something like this:
quat formQuaternion(double x, double y, double z, double angle){
quat out;
//x, y, and z form a normalized vector which is now the axis of rotation.
out.w = cosf( fAngle/2)
out.x = x * sinf( fAngle/2 )
out.y = y * sinf( fAngle/2 )
out.z = z * sinf( fAngle/2 )
return out;
}
Sorry I don't actually know the quat class you are using, but it should still have some way to set the 4 dimensions. Source: Quaternion tutorial
eulerAngleYXZ gives one possible set of euler angles which, if recombined in the order indicated by the api name, will yield the same orientation as the given quaternion.
It's not a wrong result - it's one of several correct results.
Use a quaternion to store your orientation internally - to rotate it, multiply your orientation quat by another quat representing the amount to rotate by, which can be built from angle/axis to achieve what you want.

error between two rotations?

sorry - I should know this but I don't.
I have computed the position of a reference frame (S1) with respect to a base reference frame (S0) through two different processes that give me two different 4x4 affine transformation matrices. I'd like to compute an error between the two but am not sure how to deal with the rotational component. Would love any advice.
thank you!
If R0 and R1 are the two rotation matrices which are supposed to be the same, then R0*R1' should be identity. The magnitude of the rotation vector corresponding to R0*R1' is the rotation (in radians, typically) from identity. Converting rotation matrices to rotation vectors is efficiently done via Rodrigues' formula.
To answer your question with a common use case, Python and OpenCV, the error is
r, _ = cv2.Rodrigues(R0.dot(R1.T))
rotation_error_from_identity = np.linalg.norm(r)
You are looking for the single axis rotation from frame S1 to frame S0 (or vice versa). The axis of the rotation isn't all that important here. You want the rotation angle.
Let R0 and R1 be the upper left 3x3 rotation matrices from your 4x4 matrices S0 and S1. Now compute E=R0*transpose(R1) (or transpose(R0)*R1; it doesn't really matter which.)
Now calculate
d(0) = E(1,2) - E(2,1)
d(1) = E(2,0) - E(0,2)
d(2) = E(0,1) - E(1,0)
dmag = sqrt(d(0)*d(0) + d(1)*d(1) + d(2)*d(2))
phi = asin (dmag/2)
I've left out some hairy details (and these details can bite you). In particular, the above is invalid for very large error angles (error > 90 degrees) and is imprecise for large error angles (angle > 45 degrees).
If you have a general-purpose function that extracts the single axis rotation from a matrix, use it. Or if you have a general-purpose function that extracts a quaternion from a matrix, use that. (Single axis rotation and quaternions are very closely related to one another).

Combine Rotation Axis Vectors

I'm experimenting with using axis-angle vectors for rotations in my hobby game engine. This is a 3-component vector along the axis of rotation with a length of the rotation in radians. I like them because:
Unlike quats or rotation matrices, I can actually see the numbers and visualize the rotation in my mind
They're a little less memory than quaternions or matrices.
I can represent values outside the range of -Pi to Pi (This is important if I store an angular velocity)
However, I have a tight loop that updates the rotation of all of my objects (tens of thousands) based on their angular velocity. Currently, the only way I know to combine two rotation axis vectors is to convert them to quaternions, multiply them, and then convert the result back to an axis/angle. Through profiling, I've identified this as a bottleneck. Does anyone know a more straightforward approach?
You representation is equivalent to quaternion rotation, provided your rotation vectors are unit length. If you don't want to use some canned quaternion data structure you should simply ensure your rotation vectors are of unit length, and then work out the equivalent quaternion multiplications / reciprocal computation to determine the aggregate rotation. You might be able to reduce the number of multiplications or additions.
If your angle is the only thing that is changing (i.e. the axis of rotation is constant), then you can simply use a linear scaling of the angle, and, if you'd like, mod it to be in the range [0, 2π). So, if you have a rotation rate of α raidans per second, starting from an initial angle of θ0 at time t0, then the final rotation angle at time t is given by:
θ(t) = θ0+α(t-t0) mod 2π
You then just apply that rotation to your collection of vectors.
If none of this improves your performance, you should consider using a canned quaternion library as such things are already optimized for the kinds of application you're disucssing.
You can keep them as angle axis values.
Build a cross-product (anti-symmetric) matrix using the angle axis values (x,y,z) and weight the elements of this matrix by multiplying them by the angle value. Now sum up all of these cross-product matrices (one for each angle axis value) and find the final rotation matrix by using the matrix exponential.
If matrix A represents this cross-product matrix (built from Angle Axis value) then,
exp(A) is equivalent to the rotation matrix R (i.e., equivalent to your quaternion in matrix form).
Therefore,
exp (A1 + A2) = R1 * R2
probably a more expensive calucation in the end...
You should use unit quaternions rather than scaled vectors to represent your rotations. It can be shown (not by me) that any representation of rotations using three parameters will run into problems (i.e. is singular) at some point. In your case it occurs where your vector has a length of 0 (i.e. the identity) and at lengths of 2pi, 4pi, etc. In these cases the representation becomes singular. Unit quaternions and rotation matrices do not have this problem.
From your description, it sounds like you are updating your rotation state as a result of numerical integration. In this case you can update your rotation state by converting your rotational rate (\omega) to a quaternion rate (q_dot). If we represent your quaternion as q = [q0 q1 q2 q3] where q0 is the scalar part then:
q_dot = E*\omega
where
[ -q1 -q2 -q3 ]
E = [ q0 -q3 q2 ]
[ q3 q0 -q1 ]
[ -q2 q1 q0 ]
Then your update becomes
q(k+1) = q(k) + q_dot*dt
for simple integration. You could choose a different integrator if you choose.
Old question, but another example of stack overflow answering questions the OP wasn't asking. OP already listed out his reasoning for not using quaternions to represent velocity. I was in the same boat.
That said, the way you combine two angular velocities, with each represented by a vector, which represents the axis of rotation with its magnitude representing the amount of rotation.
Just add them together. Component-by-component. Hope that helps some other soul out there.

Resources