I'm having trouble with gluLookAt.
My camera can rotate along the X and Y axis by piping in relative mouse motion events. The problem is the Z axis - I don't know how to calculate it.
So, my camera can look up, down, left and right. But I can't work out how to completely rotate through 360 degrees!
Could anyone help?
EDIT:
So, here's a trivial example of my code so far:
Point3 test(0,0,0);
Matrix4 camera = Camera::getInstance().getCameraM();
if ((event.motion.xrel > 200) || (event.motion.yrel > 200))
{
break;
}
float mx = (event.motion.xrel);
float my = -(event.motion.yrel);
mx /= 20;
my /= 20;
test.setX(test.getX()+mx);
test.setY(test.getY()+my);
Camera::getInstance().lookAt(Point3(0,0,15),test,Vector3(0,-1,0));
Camera::lookAt simply wraps up the glu lookAt function.
gluLookAt (...) produces an orthogonal view-space. You already know one of the two axes when you call it (Y-axis is given as up), the Z-axis is computed given the direction from eye to center and then finally the X-axis is the cross product between Y and Z.
By the way, you cannot completely rotate through 360 degrees. At +/- 90 degrees off of horizontal you run into a singularity, where there are two possible ways to represent the same angle. If you interpolate rotation through that Euler angle, then you can wind up flipping your orientation. Effectively if two of your three axes become parallel (which happens when you look straight up or straight down) then things get really messy with Euler angles.
Related
I'm trying to use quaternions to do rotation animation.
My algorithm creates Quaternions, and slerps every frame.
Here is my code to construct a quaternion by the axis and the rotation angle.
template <typename U>
Quaternion(Vector3<U> vec, const float& angle)
{
vec.normalize();
float cosa = cos(angle/2);
float sina = sin(angle/2);
w = cosa;
x = sina * vec.x;
y = sina * vec.y;
z = sina * vec.z;
}
Then I found that when I tried to rotate 4π radians, the animation does not work because the quaternion I created is equivalent to 0 degrees.
I wonder if quaternions can represent rotations over 360 degrees? Or is my animation algorithm in need of improvement?
I wonder if quaternions can represent rotations over 360 degrees?
No, it can not.
Quaternions between the range [360;720] will treated as rotations at the other direction: [-360;0].
And quaternions between the range [720*k; 720*(k+1)] will be treated as rotations [0;720].
If you use slerp for this kind of animation, quaternions are not good for them.
Quaternions can only slerp between angles which are smaller than 360.
If you still want to do this, use a different representation, like axis-angle.
Rotating be 360 degrees is the same as rotating by 0 degrees. To rotate by an angle alpha bigger than 360 simply rotate by alpha-360 or more general by alpha % 360.
(360 used as synonym for 2pi, you need to take care about degree vs radians of course. And not sure if thats a typo, but 360 degree is 2pi not 4pi)
PS: Actually I think there is nothing wrong with your code, and maybe you dont have to change anything. It's just your expectations that were wrong: You should get the same for a rotation by 4pi as for a rotation by 0.
Think of quaternions as instant rotations - rotating by 4π radians instantly is the same as doing nothing.
This is not what you want when you animate rotation of 4π radians over 20 seconds. You can solve it by creating an Euler Vector (a 3D vector whose direction represents the axis of rotation, same as in quaternion, while its length represents the speed/angle of the rotation), see https://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation. Later, multiply it by time passed and convert it into quaternion or 3D matrix depending on what your graphics wants.
I want to rotate my object,when I use glm::rotate.
It can only rotate on X,Y,Z arrows.
For example,Model = vec3(5,0,0)
if i use Model = glm::rotate(Model,glm::radians(180),glm::vec3(0, 1, 0));
it become vec3(-5,0,0)
i want a API,so i can rotate on vec3(0,4,0) 180 degree,so the Model move to vec3(3,0,0)
Any API can I use?
Yes OpenGL uses 4x4 uniform transform matrices internally. But the glRotate API uses 4 parameters instead of 3:
glMatrixMode(GL_MODELVIEW);
glRotatef(angle,x,y,z);
it will rotate selected matrix around point (0,0,0) and axis [(0,0,0),(x,y,z)] by angle angle [deg]. If you need to rotate around specific point (x0,y0,z0) then you should also translate:
glMatrixMode(GL_MODELVIEW);
glTranslatef(+x0,+y0,+z0);
glRotatef(angle,x,y,z);
glTranslatef(-x0,-y0,-z0);
This is old API however and while using modern GL you need to do the matrix stuff on your own (for example by using GLM) as there is no matrix stack anymore. GLM should have the same functionality as glRotate just find the function which mimics it (looks like glm::rotate is more or less the same). If not you can still do it on your own using Rodrigues rotation formula.
Now your examples make no sense to me:
(5,0,0) -> glm::rotate (0,1,0) -> (-5,0,0)
implies rotation around y axis by 180 degrees? well I can see the axis but I see no angle anywhere. The second (your desired API) is even more questionable:
(4,0,0) -> wanted API -> (3,0,0)
vectors should have the same magnitude after rotation which is clearly not the case (unless you want to rotate around some point other than (0,0,0) which is also nowhere mentioned. Also after rotation usually you leak some of the magnitude to other axises your y,z are all zero that is true only in special cases (while rotation by multiples of 90 deg).
So clearly you forgot to mention vital info or do not know how rotation works.
Now what you mean by you want to rotate on X,Y,Z arrows? Want incremental rotations on key hits ? or have a GUI like arrows rendered in your scene and want to select them and rotate if they are drag?
[Edit1] new example
I want a API so I can rotate vec3(0,4,0) by 180 deg and result
will be vec3(3,0,0)
This is doable only if you are talking about points not vectors. So you need center of rotation and axis of rotation and angle.
// knowns
vec3 p0 = vec3(0,4,0); // original point
vec3 p1 = vec3(3,0,0); // wanted point
float angle = 180.0*(M_PI/180.0); // deg->rad
// needed for rotation
vec3 center = 0.5*(p0+p1); // vec3(1.5,2.0,0.0) mid point due to angle = 180 deg
vec3 axis = cross((p1-p0),vec3(0,0,1)); // any perpendicular vector to `p1-p0` if `p1-p0` is parallel to (0,0,1) then use `(0,1,0)` instead
// construct transform matrix
mat4 m =GLM::identity(); // unit matrix
m = GLM::translate(m,+center);
m = GLM::rotate(m,angle,axis);
m = GLM::translate(m,-center); // here m should be your rotation matrix
// use transform matrix
p1 = m*p0; // and finaly how to rotate any point p0 into p1 ... in OpenGL notation
I do not code in GLM so there might be some little differencies.
Suppose I have this chicken model who I want to constantly look towards the viewer (camera position), or more easily, towards the origin (0,0,0).
How do I calculate the angles for each axis so that I can rotate the object with them?
Edit:
Sorry if my question was too general. I'm still struggling with this though.
Let's say that the 3D model position is (x,y,z) in model space, and I want the model to "look" towards the origin.
My first thoughts were to begin to rotate around the x axis (rotate vertically):
Consider the yellow circle as the y plane.
So I tried the following code, which doesn't rotate the model at all.
glm::vec3 camPos = camera.GetPosition();
float value = camPos.y / glm::sqrt(glm::pow(camPos.x,2.0f) + glm::pow(camPos.y, 2.0f) + glm::pow(camPos.z, 2.0f));
float angle = glm::asin(value);
cow.SetModelMatrix(glm::translate(camPos - glm::vec3(0,0,1.5)) * //then translate so the cow will appear a little bit infront of the camera
glm::rotate(glm::radians(angle), glm::vec3(-1,0,0)) *//then rotate vertically by the angle
glm::scale(glm::vec3(0.1, 0.1, 0.1)) //first scale, cause the cow (i mean chicken) is too big
);
The camera starts at position (0, 0, 5), looking towards the negative z axis.
What am I doing wrong?
If the chicken is at the origin c=(0,0,0) and the camera is at r=(x,y,z) and ground is at y=0. Then what you want is a sequence of rotations to get the local x axis of the chicken pointed towards the camera.
First orient your x axis on the plane with a rotation about the vertical y axis with an angle φ=-ATAN(z/x) and then a rotation about the z axis with an angle ψ=ATAN(y/√(x^2+z^2))
This creates a 3×3 rotation matrix E = ROT_Y(φ)*ROT_Z(ψ)
| x/d -x*y/(d*√(x^2+z^2)) -z/√(x^2+z^2) |
E = | y/d √(x^2+z^2)/d 0 |
| z/d -y*z/(d*√(x^2+z^2)) x/√(x^2+z^2) |
where d=√(x^2+y^2+z^2). You see the local x axis (the first column of E) pointing towards (x,y,z). Also the local z axis has no component on the vertical, so it always lies on the ground plane.
But this depend on the implementation, like if you need to keep the chicken y vertical (as opposed to keeping z in the ground plane) you will need a different set of rotations and angles. So to fully answer you need to provide more information.
I am currently trying to use OpenGL (With SDL) to draw a cube to the location where I left click in the screen and then get it to point at the position in the screen where I right click.
I can successfully draw a cube at my desired location using gluUnproject - Meaning I already know the coordinates of which my cube is situated.
However I do not know how to calculate all of the angles required to make my cube point at the new location.
Of course I am still using gluUnproject to find the coordinates of my right click, but I only know how to rotate around the Z axis from using 2D graphics.
For example before, if I wanted to rotate a quad around the Z axis (Of course, this would be a top down view where the Z axis is still "going through" the screen) in 2D I would do something like:
angle = atan2(mouseCoordsY - quadPosY, mouseCoordsX - quadPosX);
glRotatef(angle*180/PI, 0, 0, 1);
My question is, how would I go about doing this in 3D?
Do I need to calculate the angles for each axis as I did above?
If so how do I calculate the angle for rotation around the X and Y axis?
If not, what method should I use to achieve my desired results?
Any help is greatly appreciated.
If your cube is at A = (x0,y0,z0)
If your cube is currently looking at B=(x1,y1,z1)
and if you want it to look at C=(x2,y2,z2) then;
let v1 be the vector from A to B
v1 = B - A
and v2 be the one from A to C
v2 = C - A
First normalize them.
v1 = v1 / |v1|
v2 = v2 / |v2|
then calculate the rotation angle and the rotation axis as
angle = acos(v1*v2) //dot product
axis = v1 X v2 //cross product
You can call glRotate with
glRotate(angle, axis[0], axis[1], axis[2])
(This is all in ortho mode, origin is in the top left corner, x is positive to the right, y is positive down the y axis)
I have a rectangle in world space, which can have a rotation m_rotation (in degrees).
I can work with the rectangle fine, it rotates, scales, everything you could want it to do.
The part that I am getting really confused on is calculating the rectangles world coordinates from its local coordinates.
I've been trying to use the formula:
x' = x*cos(t) - y*sin(t)
y' = x*sin(t) + y*cos(t)
where (x, y) are the original points,
(x', y') are the rotated coordinates,
and t is the angle measured in radians
from the x-axis. The rotation is
counter-clockwise as written.
-credits duffymo
I tried implementing the formula like this:
//GLfloat Ax = getLocalVertices()[BOTTOM_LEFT].x * cosf(DEG_TO_RAD( m_orientation )) - getLocalVertices()[BOTTOM_LEFT].y * sinf(DEG_TO_RAD( m_orientation ));
//GLfloat Ay = getLocalVertices()[BOTTOM_LEFT].x * sinf(DEG_TO_RAD( m_orientation )) + getLocalVertices()[BOTTOM_LEFT].y * cosf(DEG_TO_RAD( m_orientation ));
//Vector3D BL = Vector3D(Ax,Ay,0);
I create a vector to the translated point, store it in the rectangles world_vertice member variable. That's fine. However, in my main draw loop, I draw a line from (0,0,0) to the vector BL, and it seems as if the line is going in a circle from the point on the rectangle (the rectangles bottom left corner) around the origin of the world coordinates.
Basically, as m_orientation gets bigger it draws a huge circle around the (0,0,0) world coordinate system origin. edit: when m_orientation = 360, it gets set back to 0.
I feel like I am doing this part wrong:
and t is the angle measured in radians
from the x-axis.
Possibly I am not supposed to use m_orientation (the rectangles rotation angle) in this formula?
Thanks!
edit: the reason I am doing this is for collision detection. I need to know where the coordinates of the rectangles (soon to be rigid bodies) lie in the world coordinate place for collision detection.
What you do is rotation [ special linear transformation] of a vector with angle Q on 2d.It keeps vector length and change its direction around the origin.
[linear transformation : additive L(m + n) = L(m) + L(n) where {m, n} € vector , homogeneous L(k.m) = k.L(m) where m € vector and k € scalar ] So:
You divide your vector into two pieces. Like m[1, 0] + n[0, 1] = your vector.
Then as you see in the image, rotation is made on these two pieces, after that your vector take
the form:
m[cosQ, sinQ] + n[-sinQ, cosQ] = [mcosQ - nsinQ, msinQ + ncosQ]
you can also look at Wiki Rotation
If you try to obtain eye coordinates corresponding to your object coordinates, you should multiply your object coordinates by model-view matrix in opengl.
For M => model view matrix and transpose of [x y z w] is your object coordinates you do:
M[x y z w]T = Eye Coordinate of [x y z w]T
This seems to be overcomplicating things somewhat: typically you would store an object's world position and orientation separately from its set of own local coordinates. Rotating the object is done in model space and therefore the position is unchanged. The world position of each coordinate is the same whether you do a rotation or not - add the world position to the local position to translate the local coordinates to world space.
Any rotation occurs around a specific origin, and the typical sin/cos formula presumes (0,0) is your origin. If the coordinate system in use doesn't currently have (0,0) as the origin, you must translate it to one that does, perform the rotation, then transform back. Usually model space is defined so that (0,0) is the origin for the model, making this step trivial.