Rotation of camera used for perspective projection - c++

I've just started playing with OpenGl to render a number of structure each comprising a number of polygon.
Basically I want to perform the equivalent of setting a camera at (0,0,z) in the world (structure) coordinates and rotate it about the x,y and z-axes of the world axes (in that order!) to render a view of each structure (as I understand it it common practice to do use the inverse camera matrix). Thus as I understand it I need to translate (to world origin i.e. (0,0,-z)) * rotateZrotateYrotateX * translate (re-define world origin see below)
So I think I need something like:
//Called when the window is resized
void handleResize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(9.148, (double)w / (double)h, 800.0, 1500.0);
}
float _Zangle = 10.0f;
float _cameraAngle = 90.0f;
//Draws the 3D scene
void drawScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
glLoadIdentity(); //Reset the drawing perspective
glTranslatef(0.0f, 0.0f, -z); //Move forward Z (mm) units
glRotatef(-_Zangle, 0.0f, 0.0f, 1.0f); //Rotate "camera" about the z-axis
glRotatef(-_cameraAngle, 0.0f, 1.0f, 0.0f); //Rotate the "camera" by camera_angle about y-axis
glRotatef (90.0f,1.0f,0.0f,0.0f); // rotate "camera" by 90 degrees about x-axis
glTranslatef(-11.0f,189.0f,51.0f); //re-define origin of world coordinates to be (11,-189,-51) - applied to all polygon vertices
glPushMatrix(); //Save the transformations performed thus far
glBegin(GL_POLYGON);
glVertex3f(4.91892,-225.978,-50.0009);
glVertex3f(5.73534,-225.978,-50.0009);
glVertex3f(6.55174,-225.978,-50.0009);
glVertex3f(7.36816,-225.978,-50.0009);
.......// etc
glEnd();
glPopMatrix();
However when I compile and run this the _angle and _cameraAngle seem to be reversed i.e. _angle seems to rotate about y-axis (Vertical) of Viewport and _cameraAngle about z-axis (into plane of Viewport)? What am I doing wrong?
Thanks for taking the time to read this

The short answer is: Use gluLookAt(). This utility function creates the proper viewing matrix.
The longer answer is that each OpenGL transformation call takes the current matrix and multiplies it by a matrix built to accomplish the transformation. By calling a series of OpenGL transformation function you build one transformation matrix that will apply the combination of transformations. Effectively, the matrix will be M = M1 * M2 * M3 . . . Mathematically, the transformations are applied from right to left in the above equation.
Your code doesn't move the camera. It stays at the origin, and looks down the negative z-axis. Your transformations move everything in model space to (11,-189,-51), rotates everything 90 degrees about the x-axis, rotates everything 90 degrees about the y-axis, rotates everything 10 degrees about the z-axis, then translates everything -z along the z-axis.
EDIT: More information
I'm a little confused about what you want to accomplish, but I think you want to have elements at the origin, and have the camera look at those elements. The eye coordinates would be where you want the camera, and the center coordinates would be where you want the objects to be. I'd use a little trigonometry to calculate the position of the camera, and point it at the origin.
In this type of situation I usually keep track of camera position using longitude, latitude, and elevation centered on the origin. Calculating x,y,z for the eye coordinates is simplyx = elv * cos(lat) * sin(lon), y = elv * sin(lat), z = elv * cos(lat) * cos(lat).
My gluLookAt call would be gluLookAt(x, y, z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
You could rotate the up on the camera by changing the last three coordinates for gluLookAt.

The z axis is coming from the center of the monitor into you. So, rotating around the z-axis should make the camera spin in place (like a 2D rotation on just the xy plane). I can't tell, but is that what's happening here?

It's possible that you are encountering Gimbal Lock. Try removing one of the rotations and see if things work the way they should.
While it's true that you can't actually move the camera in OpenGL, you can simulate camera motion by moving everything else. This is why you hear about the inverse camera matrix. Instead of moving the camera by (0, 0, 10), we can move everything in the world by (0, 0, -10). If you expand those out into matrices, you will find that they are inverses of each other.
I also noticed that, given the code presented, you don't need the glPushMatrix()/glPopMatrix() calls. Perhaps there is code that you haven't shown that requires them.
Finally, can you provide an idea of what it is you are trying to render? Debugging rotations can be hard without some context.

Short answer :Good tip
Longer answer: Yes the order of matrix multiplication is clear... that's what I meant by inverse camera matrix to indicate moving all the world coordinates of structures into the camera coordinates (hence the use of "camera" in my comments ;-)) instead of actually translating and rotating camera into the world coordinates.
So if I read between the lines correctly you suggest something like:
void drawScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
glLoadIdentity(); //Reset the drawing perspective
gluLookAt(0.0,0.0,z,11.0,-189.0,-51.0,0.0,1.0,0.0); //eye(0,0,z) look at re-defined world origin(11,-189,-51) and up(0.0,1.0,0.0)
glRotatef(-_Zangle, 0.0f, 0.0f, 1.0f); //Rotate "camera" (actually structures) about the z-axis
glRotatef(-_cameraAngle, 0.0f, 1.0f, 0.0f); //Rotate the "camera" (actually structures!) by camera_angle about y-axis
glRotatef (90.0f,1.0f,0.0f,0.0f); // rotate "camera" (actually structures) by 90 degrees about x-axis
glPushMatrix();
Or am I still missing something?

I think you are mixing axes of your world with axes of the camera,
GLRotatef only uses axes of the camera, they are not the same as your the world axes once the camera is rotated.

Related

OpenGL Translate Point With Mouse Drag

I'm using the GLM library for OpenGL to render shapes relative to a camera using perspective and modelview matrices. The three camera vectors are as follows:
glm::vec3 cam_pos(0.0f, 0.0f, 20.0f); // e | Position of camera
glm::vec3 cam_look_at(0.0f, 0.0f, -1.0f); // d | This is where the camera looks at
glm::vec3 cam_up(0.0f, 1.0f, 0.0f); // up | What orientation "up" is
The perspective and modelviews are generated via glm functions as follows:
P = glm::perspective(fov, (float)width / (float)height, 0.1f, 1000.0f);
V = glm::lookAt(cam_pos, cam_look_at, cam_up);
I have implemented trackball camera movement to adjust the direction at which the camera is looking.
Now, I'm trying to code a draggable point object that follows the mouse movement according to which way the camera is looking. However, I'm having trouble with the math for doing this.
How would I get the translation matrix to translate the point with inside the GLFW mouse callback given the displacement of the mouse in x and y coordinates and the three camera vectors?
Translate your 3 camera vectors to a Rotation-Translation Matrix (RT). With this RT matrix your can now linear transform any rigid object (basically anything in computer graphics) with respect to your camera location and pose.
Have a look at this http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
Hope this approach helps.

OpenGL Orthographical Projection

I have been playing around with OpenGL and matrix operations and I understand the concept of P * V * M but I cannot understand why changing the Z position of the 'camera' does not have the effect of zooming.
When using a perspective projection, changing the Z of the camera has the effect of zoom (as i'd expect).
glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
glm::mat4 View = glm::lookAt(
glm::vec3(0,0,3), // changing 3 to 8 will zoom out
glm::vec3(0,0,0),
glm::vec3(0,1,0)
);
glm::mat4 Model = glm::mat4(1.0f);
gml::mat4 MVP = Projection * View * Model;
However, when I use an ortho projection, changing the 3 to 8 or anything it does not have the effect of zooming out. I know they are very different projections but I am looking for an explanation (the math behind why it doesn't work would be especially helpful).
glm::mat4 Projection = glm::ortho(
0.0f,
128.0f,
0.0f,
72.0f,
0.0f,
100.0f
);
That's how orthographic projections work. Let's start with a perspective transform:
You get the projection of an object by following a straight line to the camera:
If you move the camera closer, then you will see that the projected area increases:
Orthographic projections work differently. You get the projection by following a straight line that is perpendicular to the image plane:
And obviously, the size of the projected area does not depend on how far the camera is away from the object. That's because the projection lines will always be parallel and preserve the size of the object in the two directions of the image plane.
When you change the Z coordinate from 3 to 8, you're not actually zooming out, you're just moving the camera farther away. You can zoom out without moving the camera by changing the first parameter for glm::perspective.
An orthographic camera doesn't have a location (you can think of it as infinitely far away), so it's not possible to "move" an orthographic camera in the same way. You can zoom out by changing the bounds passed to glm::ortho. Simply pass larger numbers to glm::ortho.
Look at what happens, when you move perspective camera:
Here: (xe, ye, ze) - point in eye coordinate system. (xp, yp, zp) - projection of that point
n - distance to near plane
t - distance to top plane of frustrum
You can see, that when you approach camera, xp and yp will grow.
In contrast, changing z position of orthogonal camera won't effect xp and yp, but still will effect zp, thus will change value in depth buffer.

Rotating 2D camera to space ship's heading in OpenGL (OpenTK)

The game is a top-down 2D space ship game -- think of "Asteroids."
Box2Dx is the physics engine and I extended the included DebugDraw, based on OpenTK, to draw additional game objects. Moving the camera so it's always centered on the player's ship and zooming in and out work perfectly. However, I really need the camera to rotate along with the ship so it's always facing in the same direction. That is, the ship will appear to be frozen in the center of the screen and the rest of the game world rotates around it as it turns.
I've tried adapting code samples, but nothing works. The best I've been able to achieve is a skewed and cut-off rendering.
Render loop:
// Clear.
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
// other rendering omitted (planets, ships, etc.)
this.OpenGlControl.Draw();
Update view -- centers on ship and should rotate to match its angle. For now, I'm just trying to rotate it by an arbitrary angle for a proof of concept, but no dice:
public void RefreshView()
{
int width = this.OpenGlControl.Width;
int height = this.OpenGlControl.Height;
Gl.glViewport(0, 0, width, height);
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
float ratio = (float)width / (float)height;
Vec2 extents = new Vec2(ratio * 25.0f, 25.0f);
extents *= viewZoom;
// rotate the view
var shipAngle = 180.0f; // just a test angle for proof of concept
Gl.glRotatef(shipAngle, 0, 0, 0);
Vec2 lower = this.viewCenter - extents;
Vec2 upper = this.viewCenter + extents;
// L/R/B/T
Glu.gluOrtho2D(lower.X, upper.X, lower.Y, upper.Y);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
}
Now, I'm obviously doing this wrong. Degrees of 0 and 180 will keep it right-side-up or flip it, but any other degree will actually zoom it in/out or result in only blackness, nothing rendered. Below are examples:
If ship angle is 0.0f, then game world is as expected:
Degree of 180.0f flips it vertically... seems promising:
Degree of 45 zooms out and doesn't rotate at all... that's odd:
Degree of 90 returns all black. In case you've never seen black:
Please help!
Firstly the 2-4 arguments are the axis, so please state them correctly as stated by #pingul.
More importantly the rotation is applied to the projection matrix.
// L/R/B/T
Glu.gluOrtho2D(lower.X, upper.X, lower.Y, upper.Y);
In this line your Orthogonal 2D projection matrix is being multiplied with the previous rotation and applied to your projection matrix. Which I believe is not what you want.
The solution would be move your rotation call to a place after the model view matrix mode is selected, as below
// L/R/B/T
Glu.gluOrtho2D(lower.X, upper.X, lower.Y, upper.Y);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
// rotate the view
var shipAngle = 180.0f; // just a test angle for proof of concept
Gl.glRotatef(shipAngle, 0.0f, 0.0f, 1.0f);
And now your rotations will be applied to the model-view matrix stack. (I believe this is the effect you want). Keep in mind that glRotatef() creates a rotation matrix and multiplies it with the matrix at the top of the selected stack stack.
I would also strongly suggest you move away from fixed function pipeline if possible as suggested by #BDL.

Proper gluLookAt for gluCylinder

I'm trying to draw a cylinder in a specific direction with gluCylinder. To specify the direction I use gluLookAt, however, as so many before me, I am not sure about the "up" vector and thus can't get the cylinder to point to the correct direction.
I've read from another SO answer that
The intuition behind the "up" vector in gluLookAt is simple: Look at anything. Now tilt your head 90 degrees. Where you are hasn't changed, the direction you're looking at hasn't changed, but the image in your retina clearly has. What's the difference? Where the top of your head is pointing to. That's the up vector.
It is a simple explanation but in the case of my cylinder I feel like the up vector is totally unimportant. Since a cylinder can be rotated around its axis and still look the same, a different up vector wouldn't change anything. So there should be infinitely many valid up vectors for my problem: all orthogonals to the vector from start point to end point.
So this is what I do:
I have the world coordinates of where the start-point and end-point of the cylinder should be, A_world and B_world.
I project them to viewport coordinates A_vp and B_vp with gluProject:
GLdouble A_vp[3], B_vp[3], up[3], model[16], projection[16];
GLint gl_viewport[4];
glGetDoublev(GL_MODELVIEW_MATRIX, &model[0]);
glGetDoublev(GL_PROJECTION_MATRIX, &projection[0]);
glGetIntegerv(GL_VIEWPORT, gl_viewport);
gluProject(A_world[0], A_world[1], A_world[2], &model[0], &projection[0], &gl_viewport[0], &A_vp[0], &A_vp[1], &A_vp[2]);
gluProject(B_world[0], B_world[1], B_world[2], &model[0], &projection[0], &gl_viewport[0], &B_vp[0], &B_vp[1], &B_vp[2]);
I call glOrtho to reset the camera to its default position: Negative z into picture, x to the right, y up:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, vp_edgelen, vp_edgelen, 0, 25, -25);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
I translate to coordinate A_vp, calculate the up vector as the normal to the vector A_vp — B_vp and specify the view with gluLookAt:
glTranslatef(A_vp[0], gl_viewport[2] - A_vp[1], A_vp[2]);
glMatrixMode(GL_MODELVIEW);
GLdouble[] up = {A_vp[1] * B_vp[2] - A_vp[2] * B_vp[1],
A_vp[2] * B_vp[0] - A_vp[0] * B_vp[2],
A_vp[0] * B_vp[1] - A_vp[1] * B_vp[0]};
gluLookAt(0, 0, 0,
B_vp[0], gl_viewport[2] - B_vp[1], B_vp[2],
up[0], up[1], up[2]);
I draw the cylinder with gluCylinder:
GLUquadricObj *gluCylObj = gluNewQuadric();
gluQuadricNormals(gluCylObj, GLU_SMOOTH);
gluQuadricOrientation(gluCylObj, GLU_OUTSIDE);
gluCylinder(gluCylObj, 10, 10, 50, 10, 10);
Here is the unexpected result:
Since the cylinder starts at the correct position and since I was able to draw a circle at position B_vp, the only thing that must be wrong is the "up" vector in gluLookAt, right?
gluLookAt() is not necessary to achieve the proper perspective. It is enough to rotate the current z-vector to point to the direction the cylinder should point.

Using glTranslate and glRotate to move and rotate?

I am writing an application that draws a 2D triangle and rotates it around its z-axis depending on its position. Its middle (t1.tx, t1.ty) is constantly being changed when the triangle is dragged with the mouse. The problem is that when I drag the triangle to another location, instead of staying where its at and rotating, it rotates in a circle path around its center point.
What I am doing wrong? I want it to rotate in its position.
void drawTriangle() {
glBegin(GL_POLYGON);
glColor3f((float)200/255, (float)200/255, (float)200/255);
glVertex2f(t1.tx, t1.ty + .2); // top point of triangle
glVertex2f(t1.tx - .2, t1.ty - .2); // left point
glVertex2f(t1.tx + .2, t1.ty - .2); // right point
glEnd();
}
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
glTranslatef(t1.tx, t1.ty, 0); // move matrix to triangle's current center point
glRotatef(theta, 0, 0, 1.0); // rotate on z-axis
drawTriangle();
glPopMatrix();
glutPostRedisplay();
glutSwapBuffers();
}
It has been a WHILE since I did this stuff, but based on what you are saying, my first guess would be to swap the order of the glTranslatef and glRotatef. You want to rotate in model space then transform to world space.
I fixed it. The problem was that I was translating the matrix, and then drawing onto that matrix that was translated. What I did was draw my triangle at the center of the matrix, so it still moves and rotates at its current position as the matrix is translated. Thank you for the help.