I'm trying to move around a cube centered at the origin using gluLookAt instead of performing the R*T transformation directly on the object. At first, I was not sure how to do this problem. Then I realized (after implementing half of a solution in circular coords) that I should try using a spherical coordinate system representation. I was able to write some code to do this, but I (the 'eye' of the camera) spin really quickly around the cube. Also, I notice that I'm moving a bit closer to the cube as well instead of holding a constant radius. When I use the Rotate*Translate method, it spins at a more proper rate with the same distance.
My approach for rotation was to use spherical coordinates, but I'm not sure if this is correct. I calculate the two angles, according to the diagram located on the Wikipedia page. I calculate the magnitude of the current point I'm at (distX, distY, distZ). We are initially in the XY plane looking at the negative Z plane). I'm also given two angles, one to specify the angle of rotation about the x-axis and one for the y-axis. These five values are calculated based on how my mouse clicks are made.
I calculate theta and phi based off of some trig and then finally the new position. These formulae can be found in that link earlier. The last step is to plug it into gluLookAt. Again, upon running this program, I am able to spin around the cube, but it is very fast and translates the camera as well. What am I doing wrong?
My code is listed below in case you want to reference it. The part referencing 'y' and 'up' are my attempts to calculate the rotation about the Z axis. The best way I can describe that rotation that is if you look at an object, imagine yourself the camera, and tilt your head left and right. I did not include it in my call to gluLookAt since I couldn't get that to work either.
EDIT: It now rotates at what appears to be the correct rate, but it does not rotate completely. By this, I mean that upon rotating, the "eye" moves towards the cube with an angle, but then will swivel back away, undoing the rotation. But this loops so the path taken by the eye looks similar to a bent infinity symbol.
void sceneTransformation(){
glLoadIdentity( );
//Using the R*T approach. Works flawlessly
//glTranslatef(-distX, distY, -distZ);
//glRotatef( anglex, 1.0, 0.0, 0.0 );
//glRotatef( angley, 0.0, 1.0, 0.0 );
GLdouble radx = anglex*PI/180.;
GLdouble rady = angley*PI/180.;
GLdouble l = sqrt(pow(distX, 2) + pow(distY, 2) + pow(distZ, 2));
GLdouble phi = atan(distY/distZ) + radx;
GLdouble theta = acos(distZ/l) + radx;
GLdouble deltaZ = l*sin(theta)*cos(phi);
GLdouble deltaY = l*sin(theta)*sin(phi);
GLdouble deltaX = l*cos(theta);
GLdouble ytheta = atan(distX/distY);
GLdouble y = sqrt(pow(distX,2) + pow(distY, 2));
GLdouble yangle = PI/2.-ytheta-rady;
GLdouble up_y = y*sin(yangle);
GLdouble up_x = y*cos(yangle);
gluLookAt((distX - deltaX), (distY - deltaY), (distZ - deltaZ), 0, 0, 0, 0,1,0);
}
The math functions like sin(), cos(), and friends expect input in radians, not degrees. It looks like you're assuming degrees, so you're spinning roughly 57.3 times (exactly: 360 / (2 * pi)) faster than intended.
You need to make your movement timebased, this means to multiply the rotation angle with the delta between the beginning last and current frame.
angle += 90 * timeDelta; // Rotate by 90 degree each second
There is a fine solution to your problem. take a bit of investigation, but I think it will meet your needs.
Quaternions
Some code for it
Good for transforming vector to rotation and backwards
Related
I'm trying to implement a camera that follows a moving object. I've implemented these functions:
void Camera::espheric_yaw(float degrees, glm::vec3 center_point)
{
float lim_yaw = glm::radians(89.0f);
float radians = glm::radians(degrees);
absoluteYaw += radians;
... clamp absoluteYaw
float radius = 10.0f;
float camX = cos(absoluteYaw) * cos(absoluteRoll) * radius;
float camY = sin(absoluteRoll)* radius;
float camZ = sin(absoluteYaw) * cos(absoluteRoll) * radius;
eyes.x = camX;
eyes.y = camY;
eyes.z = camZ;
lookAt = center_point;
view = glm::normalize(lookAt - eyes);
up = glm::vec3(0, 1, 0);
right = glm::normalize(glm::cross(view, up));
}
I want to use this function (and the pitch version) for a camera that follows a moving 3d model. Right now, it works when the center_point is the (0,1,0). I think i'm getting the position right but the up vector is clearly not always (0,1,0).
How can I get my up, view and right vector for the camera? And then, if I update the eyes position of the camera this way, how will my camera move when the other object (centered at center_position parameter) moves?
The idea is to update this each time I have mouse input with centered_value = center of the moving object. Then use gluLookAt with view, eyes and up values of my camera (and lookAt which will be eyes+view).
Following a moving object is matter of pointing the camera to that object. This is what typical lookAt function does. See the maths here and then use glm::lookAt().
The 'Arcball' technic is for rotating with the mouse. See some maths here.
The idea is to get two vectors (first, second) from positions on screen. For each vector, X,Y are taking depending on pixels "travelled" by mouse and the size of the window. Z is calculated by 'trackball' maths. With these two vectors (after normalizing them), its cross product gives the axis of rotation in camera coordinates, and its dot product gives the angle. Now, you can rotate the camera by glm::rotate()
If you go another route (e.g. calculating camera matrix on your own), then the "up" direction of the camera must be updated by yourself. Remember it's perpendicular to the other two axis of the camera.
I am trying to implement a camera which orbits around the origin, where I have successfully implemented the ability to yaw using the gluLookat function. I am trying to implement pitch, but have a few issues with the outcome (pitch only works if I yaw to a certain point and then pitch).
Here is my attempt so far:
float distance, // radius (from origin) updated by -, + keys
pitch, // angle in degrees updated from W, S keys (increments of +- 10)
yaw; // angle in degrees updated from A, D keys (increments of +- 10)
view = lookAt(
Eigen::Vector3f(distance * sin(toRadians(pitch)) * cos(toRadians(yaw)), distance * sin(toRadians(pitch)) * sin(toRadians(yaw)), distance * cos(toRadians(pitch))),
Eigen::Vector3f(0.0f, 0.0f, 0.0f),
Eigen::Vector3f(0.0f, 0.0f, 1.0f));
proj = perspective(toRadians(90.0f), static_cast<float>(width) / height, 1.0f, 10.0f);
I feel like my issue is the Up vector, but I'm not sure how to update it properly(and at the same time I think its fine, as I always want the orientation of the camera to stay the same, I really just want to move the position of the camera)
Edit: I wanted to add that I'm calculating the position based info found here: http://tutorial.math.lamar.edu/Classes/CalcIII/SphericalCoords.aspx I'm not sure if the math discussed here directly translates over so please correct me if wrong.
It might be a matter of interpretation. Your code looks correct but pitch might not have the meaning that you think.
When pitch is 0, the camera is located at the north pole of the sphere (0, 0, 1). This is a bit problematic since your up-vector and view direction become parallel and you will not get a valid transform. Then, when pitch increases, the camera moves south until it reaches the south pole when pitch=PI. Your code should work for any point that is not at the poles. You might want to swap sin(pitch) and cos(pitch) to start at the equator when pitch=0 (and support positive and negative pitch).
Actually, I prefer to model this kind of camera more directly as a combination of matrices:
view = Tr(0, 0, -distance) * RotX(-pitch) * RotY(-yaw)
Tr is a translation matrix, RotX is a rotation about the x-axis, and RotY is a rotation about the y-axis. This assumes that the y-axis is up. If you want another axis to be up, you can just add an according rotation matrix. E.g., if you want the z-axis to be up, then
view = Tr(0, 0, -distance) * RotX(-pitch) * RotY(-yaw) * RotX(-Pi/2)
I want to code a first person camera with its rotation stored in a quaternion. Unfortunately there is something wrong with the rotation.
The following function is responsible to rotate the camera. The parameters Mouse and Speed pass the mouse movement and rotation speed. Then the function fetches the rotation quaternion, rotates it and stores the result. By the way, I'm using Bullet Physics that is where the types and functions come from.
void Rotate(vec2 Mouse, float Speed)
{
btTransform transform = camera->getWorldTransform();
btQuaternion rotation = transform.getRotation();
Mouse = Mouse * Speed; // apply mouse sensitivity
btQuaternion change(Mouse.y, Mouse.x, 0); // create quaternion from angles
rotation = change * rotation; // rotate camera by that
transform.setRotation(rotation);
camera->setWorldTransform(transform);
}
To illustrate the resulting camera rotation when the mouse moves, I show you a hand drawing. On the left side the wrong rotation the camera actually performs is shown. On the right side the desired correct case is shown. The arrows indicate how the camera is rotate when moving the mouse up (in orange) and down (in blue).
As you can see, as long as the yaw is zero, the rotation is correct. But the more yaw it has, the smaller the circles in which the camera rotates become. In contrast, the circles should always run along the whole sphere like a longitude.
I am not very familiar with quaternions, so here I ask how to correctly rotate them.
I found out how to properly rotate a quaternion on my own. The key was to find vectors for the axis I want to rotate around. Those are used to create quaternions from axis and angle, when angle is the amount to rotate around the actual axis.
The following code shows what I ended up with. It also allows to roll the camera, which might be useful some time.
void Rotate(btVector3 Amount, float Sensitivity)
{
// fetch current rotation
btTransform transform = camera->getWorldTransform();
btQuaternion rotation = transform.getRotation();
// apply mouse sensitivity
Amount *= Sensitivity;
// create orientation vectors
btVector3 up(0, 1, 0);
btVector3 lookat = quatRotate(rotation, btVector3(0, 0, 1));
btVector3 forward = btVector3(lookat.getX(), 0, lookat.getZ()).normalize();
btVector3 side = btCross(up, forward);
// rotate camera with quaternions created from axis and angle
rotation = btQuaternion(up, Amount.getY()) * rotation;
rotation = btQuaternion(side, Amount.getX()) * rotation;
rotation = btQuaternion(forward, Amount.getZ()) * rotation;
// set new rotation
transform.setRotation(rotation);
camera->setWorldTransform(transform);
}
Since I rarely found information about quaternion rotation, I'll spend some time further explaining the code above.
Fetching and setting the rotation is specific to the physics engine and isn't related to this question so I won't elaborate on this. The next part, multiplying the amount by a mouse sensitivity should be really clear. Let's continue with the direction vectors.
The up vector depends on your own implementation. Most conveniently, the positive Y axis points up, therefore we end up with 0, 1, 0.
The lookat vector represents the direction the camera looks at. We simply rotate a unit vector pointing forward by the camera rotation quaternion. Again, the forward pointing vector depends on your conventions. If the Y axis is up, the positive Z axis might point forward, which is 0, 0, 1.
Do not mix that up with the next vector. It's named forward which references to the camera rotation. Therefore we just need to project the lookat vector to the ground. In this case, we simply take the lookat vector and ignore the up pointing component. For neatness we normalize that vector.
The side vector points leftwards from the camera orientation. Therefore it lies perpendicular to both the up and the forward vector and we can use the cross product to compute it.
Given those vectors, we can correctly rotate the camera quaternion around them. Which you start with, Z, Y or Z, depends on the Euler angle sequence which is, again, a convention varying from application to application. Since I want to rotations to be applied in Y X Z order, I do the following.
First, rotate the camera around the up axis by the amount for the Y rotation. This is yaw.
Then rotate around the side axis, which points leftwards, by the X amount. It's pitch.
And lastly, rotate around the forward vector by the Z amount to apply roll.
To apply those rotations, we need to multiply the quaternions create by axis and angle with the current camera rotation. Lastly we apply the resulted quaternion to the body in the physics simulation.
Matrices and pitch/yaw/roll both having their limitations, I do not use them anymore but use instead quaternions. I rotate the view vector and recalculate first the camera vectors, then the view matrix in regard to the rotated view vector.
void Camera::rotateViewVector(glm::quat quat) {
glm::quat rotatedViewQuat;
quat = glm::normalize(quat);
m_viewVector = glm::normalize(m_viewVector);
glm::quat viewQuat(0.0f,
m_viewVector.x,
m_viewVector.y,
m_viewVector.z);
viewQuat = glm::normalize(viewQuat);
rotatedViewQuat = (quat * viewQuat) * glm::conjugate(quat);
rotatedViewQuat = glm::normalize(rotatedViewQuat);
m_viewVector = glm::normalize(glm::vec3(rotatedViewQuat.x, rotatedViewQuat.y, rotatedViewQuat.z));
m_rightVector = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), m_viewVector));
m_upVector = glm::normalize(glm::cross(m_viewVector, m_rightVector));
}
Hey guys I am trying to do a Camera class that uses lookAt from glm library. I have 4 points, the first one is eye, that is the camera position in space, the second one is the look, that is the point where the camera is looking at, the third one is the upp, that set the orientation of camera, and the forth is side, that is a cross product of look - eye and upp -eye.
So in the end I got a base of 3 vectors all of them with origin in the eye point. I got a coordinate system of the camera.
In my camera class, I want to be able to rotate about the coordinate system of the camera, not the coordinate system of the world. So what I am doing is rotate about one of the axis of the coordinate system of the camera.
I construct the class with initial values like this:
void Observer::initialize(glm::vec3 eye, glm::vec3 look, glm::vec3 upp, glm::vec3 side)
{
this->eye = eye; // (0.0, 0.0, 0.0)
this->look = look; // (0.0, 0.0, -1.0)
this->upp = upp; // (0.0, 1.0, 0.0)
this->side = side; // (1.0, 0.0, 0.0)
}
When I want to rotate the coordinate system about the x axis for example I call the function from glm like this:
void Observer::pitch(GLfloat pitch)
{
glm::mat4 rotate(1.0f);
rotate = glm::rotate(rotate, pitch, side - eye);
look = glm::vec3(rotate * glm::vec4(look, 1.0f));
upp = glm::vec3(rotate * glm::vec4(upp, 1.0f));
}
So far, I am understanding that all my points still form a coordinate system for the camera and all vector are perpendicular between each other.
But then I use these points I got with the lookAt function, to position the camera in the world.
glm::mat4 view = glm::lookAt(eye, look, upp);
And multiply this matrix with the modelview matrix from OpenGL
If I start to rotate a lot, the camera after a few rotations "reflect" the rotation, like I was rotating in the other way (I don't know how to describe what is really happening in a better way =s).
I really don't understand what is happening. I should normalize the vectors after I apply the rotation? Am I having a problem with gimbal lock (I don't know a lot about gimbal lock)?
When you do incremental rotations on vectors as you have done, numerical errors mount. When error causes the up and look-at vectors to point nearly in the same direction or opposite each other, the camera transformation calculation is unstable and whacky things can happen. Gimbal lock. Length changes cause different problems.
A solution that has worked for me is to re-orthogonalize the up and look-at vectors after each rotation. To do this, compute their cross-product L, then adjust (really replace) the up vector by crossing L with the lookAt. After all this re-normalize both up and look-at to unit length.
Though the orthogonalization-normalization is a fast operation, you don't really have to do it with every camera motion.
Note that when you correct the vectors like this you are actually doing part of the lookAt matrix calculation, so consider implementing your own to avoid an unnecessary cross product. See e.g. this prior SO article on this topic.
I am working on an application that has similar functionality to MotionBuilder in its viewport interactions. It has three buttons:
Button 1 rotates the viewport around X and Y depending on X/Y mouse drags.
Button 2 translates the viewport around X and Y depending on X/Y mouse drags.
Button 3 "zooms" the viewport by translating along Z.
The code is simple:
glTranslatef(posX,posY,posZ);
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
Now, the problem is that if I translate first, the translation will be correct but the rotation then follows the world axis. I've also tried rotating first:
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glTranslatef(posX,posY,posZ);
^ the rotation works, but the translation works according to world axis.
My question is, how can I do both so I achieve the translation from code snippet one and the rotation from code snippet 2.
EDIT
I drew this rather crude image to illustrate what I mean by world and local rotations/translations. I need the camera to rotate and translate around its local axis.
http://i45.tinypic.com/2lnu3rs.jpg
Ok, the image makes things a bit clearer.
If you were just talking about an object, then your first code snippet would be fine, but for the camera it's quite different.
Since there's technically no object as a 'camera' in opengl, what you're doing when building a camera is just moving everything by the inverse of how you're moving the camera. I.e. you don't move the camera up by +1 on the Y axis, you just move the world by -1 on the y axis, which achieves the same visual effect of having a camera.
Imagine you have a camera at position (Cx, Cy, Cz), and it has x/y rotation angles (CRx, CRy). If this were just a regular object, and not the camera, you would transform this by:
glTranslate(Cx, Cy, Cz);
glRotate(CRx, 1, 0, 0);
glRotate(CRy, 0, 1, 0);
But because this is the camera, we need to do the inverse of this operation instead (we just want to move the world by (-Cx, -Cy, and -Cz) to emulate the moving of a 'camera'. To invert the matrix, you just have to do the opposite of each individual transform, and do them in reverse order.
glRotate(-CRy, 0, 1, 0);
glRotate(-CRx, 1, 0, 0);
glTranslate(-Cx, -Cy, -Cz);
I think this will give you the kind of camera you're mentioning in your image.
I suggest that you bite the apple and implement a camera class that stores the current state of the camera (position, view direction, up vector, right vector) and manipulate that state according to your control scheme. Then you can set up the projection matrix using gluLookAt(). Then, the order of operations becomes unimportant. Here is an example:
Let camPos be the current position of the camera, camView its view direction, camUp the up vector and camRight the right vector.
To translate the camera by moveDelta, simply add moveDelta to camPos. Rotation is a bit more difficult, but if you understand quaternions you'll be able to understand it quickly.
First you need to create a quaternion for each of your two rotations. I assume that your horizontal rotation is always about the positive Z axis (which points at the "ceiling" if you will). Let hQuat be the quaternion representing the horizontal rotation. I further assume that you want to rotate the camera about its right axis for your vertical rotation (creating a pitch effect). For this, you must apply the horizontal rotation to the camera's current angle. The result is the rotation axis for your vertical rotation hQuat. The total rotation quaternion is then rQuat = hQuat * vQuat. Then you apply rQuat to the camera's view direction, up, and right vectors.
Quat hRot(rotX, 0, 0, 1); // creates a quaternion that rotates by angle rotX about the positive Z axis
Vec3f vAxis = hRot * camRight; // applies hRot to the camera's right vector
Quat vRot(rotY, vAxis); // creates a quaternion that rotates by angle rotY about the rotated camera's right vector
Quat rQuat = hRot * vRot; // creates the total rotation
camUp = rQuat * camUp;
camRight = rQuat * camRight;
camView = rQuat * camView;
Hope this helps you solve your problem.
glRotate always works around the origin. If you do:
glPushMatrix();
glTranslated(x,y,z);
glRotated(theta,1,0,0);
glTranslated(-x,-y,-z);
drawObject();
glPopMatrix();
Then the 'object' is rotate around (x,y,z) instead of the origin, because you moved (x,y,z) to the origin, did the rotation, and then pushed (x,y,z) back where it started.
However, I don't think that's going to be enough to get the effect you're describing. If you always want transformations to be done with respect to the current frame of reference, then you need to keep track of the transformation matrix yourself. This why people use Quaternion based cameras.