I'm trying to implement a camera in OpenGL which can move around and rotate. I'd like the camera to rotate around a point on XZ plane (Y being up) that the camera is currently looking at (that is, the intersection of the XZ plane and camera's direction). What would be the best way to achieve this?
Let's say you want to rotate your camera around a point P(x, 0, z) on the XZ plane
One way to do this is by calling glRotatef(theta, px, py, pz) which multiplies your current matrix by a rotation matrix (rotation of theta angles around the vector (px, py, pz))
Since you want to rotate around an arbitrary point and not the origin, you'll need to translate the axes first:
glTranslatef(x, 0, z);
glRotatef(thetax, 1, 0, 0);
glRotatef(thetay, 0, 1, 0);
glRotatef(thetaz, 0, 0, 1);
glTranslatef(-x, 0, -z);
Alternatively, you could use gluLookAt(cx, cy, cz, px, py, pz, ux, uy, uz), which creates a view matrix for a camera at (cx, cy, cz) which looks at (px, py, pz) with (ux, uy, uz) being the vector pointing "up" (the roll of the camera)
I suppose you'd want the distance of your camera to your point to remain static, so it would be convenient to express your camera's coordinates in the spherical coordinate system
x = rsin(θ)cos(φ)
y = rsin(θ)sin(φ)
z = rcos(θ)
Related
I am trying to use gluLookAt to implement an FPS style camera in OpenGL fixed function pipeline. The mouse should rotate the camera in any given direction.
I store the position of the camera:
float xP;
float yP;
float zP;
I store the look at coordinates:
float xL;
float yL;
float zL;
The up vector is always set to (0,1,0)
I use this camera as follows: gluLookAt(xP,yP,zP, xL,yL,zL, 0,1,0);
I want my camera to be able to be able to move along the yaw and pitch, but not roll.
After every frame, I reset the coordinates of the mouse to the middle of the screen. From this I am able to get a change in both x and y.
How can I convert the change in x and y after each frame to appropriately change the lookat coordinates (xL, yL, zL) to rotate the camera?
Start with a set of vectors:
fwd = (0, 0, -1);
rht = (1, 0, 0);
up = (0, 1, 0);
Given that Your x and y, taken from the mouse positions You mentioned, are small enough You can take them directly as yaw and pitch rotations respectively. With yaw value rotate the rht and fwd vectors over the up vector, than rotate fwd vactor over the rht with pitch value. This way You'll have a new forward direction for Your camera (fwd vactor) from which You can derive a new look-at point (L = P + fwd in Your case).
You have to remember to restrict pitch rotation not to have fwd and up vectors parallel at some point. You can prevent that by recreating the up vector every time You do pitch rotation - simply do a cross product between rht and fwd vactors. A side-note here though - this way up will not always be (0,1,0).
I managed to acquire camera's intrinsic and extrinsic parameters using OpenCV, thus I have fx, fy, cx and cy. And I also have the screen / image's width and height.
But how do I create an OpenGL perspective projection matrix from these parameters?
glFrustrum shows how to create projection matrix, given Z near, Z far and the image width and height. But how do I include focal points and camera centers in this matrix?
You can check the following links
Kyle Simek's explanation
My explanation
Here is the code to obtain the OpenGL projection matrix equivalent to a computer vision camera with camera matrix K=[fx, s, cx; 0, fy, cy; 0, 0, 1] and image size [W, H]:
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
GLdouble perspMatrix[16]={2*fx/W,0,0,0,2*s/W,2*fy/H,0,0,2*(cx/W)-1,2*(cy/H)-1,(zmax+zmin)/(zmax-zmin),1,0,0,2*zmax*zmin/(zmin-zmax),0};
glMultMatrixd(perspMatrix);
NB: zmin and zmax represent the near and far Z clipping planes. This formulation assumes that the OpenGL world coordinate frame is chosen as follows:
The OpenGL camera is assumed to be located at the origin, looking towards positive Z axis, with a down vector collinear and towards the positive Y axis.
In relation to the AldurDisciple answer this is the formulation you need to use if the world coordinate frame is choosen with inverted z axe
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
GLdouble perspMatrix[16]={2*fx/w,0,0,0,0,2*fy/h,0,0,2*(cx/w)-1,2*(cy/h)-1,-(far+near)/(far-near),-1,0,0,-2*far*near/(far-near),0};
glMultMatrixd(perspMatrix);
Open GL operates with a frustum which is related to perspective projection with some limits in depth referred as near and far values. Imagine a pyramid lying on its side - this is frustum. Another analogy is a projector beam that extends in its width and height with the distance - this is frustum too. So right, left, bottom, and top are your image coordinates while near and far are your depth limits with the near beint your focal plane. OpenGL will put Cx and Cy in the center of the image plane so you can skip them. The alternative and more natural way to specify frustum is based on viewing angle or field of view (50-60 deg is typical); the function you call is glPerspective() where you still have near and far but instead of sizes specify the angle and aspect ratio. Good luck.
You have cx, cy, fx, fy, width, height.
And you need left, right, top, bottom in your equations.
You can calculate these value using this way.
left = cx * near / -fx
top = cy * near / fy
right = -(width - cx) * near / -fx
bottom = -(height - cy) * near / fy
I have camera class that hold her orientation via euler angles and position. Something like that:
float m_x;
float m_y;
float m_z;
Vector4 m_pos;
And i want to move this camera free over the space.
When user move mouse up-down camera must rotate around x axis in her own coordinate system. But i want to hold only this three angles and position and nothing more.
So algorithm looks like this:
Find camera local system axis (u, v, n)
Rotate around u axis on angle alpha
Find angles around (1, 0, 0), (0, 1, 0), (0, 0, 1) that answer to rotation on angle alpha around u axis
Add them to m_x, m_y, m_z
Question is: how can i calculate rotation angles in default coordinate system (i mean in (1, 0, 0), (0, 1, 0) and (0, 0, 1)) that answer to rotation angles in local camera coordinate system?
Or may be better solution exists for this problem?
I'm answering the concise question from your comment:
how to calculate rotation in one coordinate system that answer to rotation in another coordinate system?
You can transform rotations between coordinate systems by applying a suitable transformation matrix. This in turn can be computed form the euler angles, see the Wikipedia section on conversion formulae.
Depending on your application, you might or might not have to take translations into account as well. As I understand your question, you can concentrate on the rotation part of each transformation.
So I currently use quaternions to store and modify the orientation of the objects in my OpenGL scene, as well as the orientation of the camera. When rotating these objects directly (i.e. saying I want to rotate the camera Z amount around the Z-axis, or I want to rotate an object X around the X-axis and then translate it T along its local Z-axis), I have no problems, so I can only assume my fundamental rotation code is correct.
However, I am now trying to implement a function to make my camera orbit an arbitrary point in space, and am having quite a hard time of it. Here is what I have come up with so far, which doesn't work (this takes place within the Camera class).
//Get the inverse of the orientation, which should represent the orientation
//"from" the focal point to the camera
Quaternion InverseOrient = m_Orientation;
InverseOrient.Invert();
///Rotation
//Create change quaternions for each axis
Quaternion xOffset = Quaternion();
xOffset.FromAxisAngle(xChange * m_TurnSpeed, 1.0, 0.0, 0.0);
Quaternion yOffset = Quaternion();
yOffset.FromAxisAngle(yChange * m_TurnSpeed, 0.0, 1.0, 0.0);
Quaternion zOffset = Quaternion();
zOffset.FromAxisAngle(zChange * m_TurnSpeed, 0.0, 0.0, 1.0);
//Multiply the change quats into the inversed orientation quat
InverseOrient = yOffset * zOffset * xOffset * InverseOrient;
//Translate according to the focal distance
//Start with a vector relative to the position being looked at
sf::Vector3<float> RelativePos(0, 0, -m_FocalDistance);
//Rotate according to the quaternion
RelativePos = InverseOrient.MultVect(RelativePos);
//Add that relative position to the focal point
m_Position.x = m_FocalPoint->x + RelativePos.x;
m_Position.y = m_FocalPoint->y + RelativePos.y;
m_Position.z = m_FocalPoint->z + RelativePos.z;
//Now set the orientation to the inverse of the quaternion
//used to position the camera
m_Orientation = InverseOrient;
m_Orientation.Invert();
What ends up happening is that the camera rotates around some other point - certainly not the object, but apparently not itself either, as though it were looping through space in a spiral path.
So this is clearly not the way to go about orbiting a camera around a point, but what is?
I would operate on the camera first in spherical coordinates and convert to quaternions as necessary.
Given the following assumptions:
The camera has no roll
The point you are looking at is [x, y, z]
You have yaw, pitch angles
[0, 1, 0] is "up"
Here is how to calculate some important values:
The view vector: v = [vx, vy, vz] = [cos(yaw)*cos(pitch), sin(pitch), -sin(yaw)*cos(pitch)]
The camera location: p = [x, y, z] - r*v
The right vector: cross product v with [0, 1, 0]
The up vector: cross product v with the right vector
Your view quaternion is [0, vx, vy, vz] (that's the view vector with a 0 w-component)
Now in your simulation you can operate on pitch/yaw, which are pretty intuitive. If you want to do interpolation, convert the before and after pitch+yaws into quaternions and do quaternion spherical linear interpolation.
I have a scene which is basically a square floor measuring 15x15 (a quad with coordinates (0,0,0) (0,0,15) (15,0,15) (15,0,0) ).
I 've set the center-of-scene to be at (7.5,0,7.5). Problem is I can't figure out how to rotate the camera horizontally around that center of scene (aka make the camera do a 360 horizontal circle around center-of-scene). I know you need to do something with sin and cos, but don't know what exactly.
Here is the code (plain C):
//set camera position
//camera height is 17
GLfloat camx=0, camy=17, camz=0;
//set center of scene
GLfloat xref=7.5, yref=0, zref=7.5;
gluLookAt(camx, camy, camz, xref, yref, zref, 0, 1, 0);
//projection is standard gluPerspective, nothing special
gluPerspective(45, (GLdouble)width/(GLdouble)height, 1, 1000);
You need to modify the camx and camz variables.
The points you want to walk through lie on the circle and their coordinates are determined by x = r*sin(alpha) + 7.5, z = r*cos(alpha) + 7,5, where r is the radius of the circle and alpha is the angle between xy plane and the current position of your camera.
Of course the angle depends on the rotation speed and also on the time from the beginning of the animation. Basically, the only thing you need to do is to set the right angle and then calculate the coordinates from the expressions above.
For more info about the circle coordinates, see Wiki : http://en.wikipedia.org/wiki/Unit_circle
I think there are two ways you can use:
You can use sin/cos to compute your camx and camz position. This picture is a good example how this works.
An alternative would be to move the camera to 7.5, 0, 7.5, then rotate the camera with the camera angle you want. After that you move the camera by -7.5, 0, -7.5.