I have this simple OpenGL code to rotate the camera in a circle around the scene. When the user presses a key, the camera moves further along the circle.
#include <GL/glut.h>
#include <stdio.h>
#include <math.h>
float X_MIN = -3.0;
float X_MAX = 3.0;
float Y_MIN = -3.0;
float Y_MAX = 3.0;
int WINDOW_WIDTH = 500;
int WINDOW_HEIGHT = 500;
float vInc = 0.01f;
// Viewing parameters
GLfloat cameraLoc[3] = {0, 8, 0};
GLfloat v = 0.0f;
GLfloat viewingRadius = 8.0f;
GLfloat xref = 0.0, yref = 0.0, zref = 0.0;
GLfloat x_up = 0.0, y_up = 1.0, z_up = 0.0;
GLfloat dnear = 1.0, dfar = 8.0;
void printMat(GLfloat mat[16])
{
printf("\n**************************************");
for (int r = 0; r < 4; r++)
{
printf("\n");
printf("%f ", mat[r]);
printf("%f ", mat[r+4]);
printf("%f ", mat[r+8]);
printf("%f ", mat[r+12]);
}
printf("\n");
}
void setupView()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Specify the viewing parameters
// view[] is the origin of the viewing frame
// ref[] is the reference position (this is NOT projection reference point)
// up[] is the view up vector
gluLookAt(cameraLoc[0], cameraLoc[1], cameraLoc[2], xref, yref, zref, x_up, y_up, z_up);
// Get and print the matrix
GLfloat mvmat[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mvmat);
printf("\nModelview matrix:");
printMat(mvmat);
// Projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(120.0, 1.0f, dnear, dfar);
// Get and print the matrix
GLfloat projmat[16];
glGetFloatv(GL_PROJECTION_MATRIX, projmat);
printf("\nProjection matrix:");
printMat(projmat);
}
// Clear buffers, setup view, enable depth
void init(void)
{
glClearColor(1.0,1.0,1.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
setupView();
}
void reshapeCb(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);
WINDOW_WIDTH = newWidth;
WINDOW_HEIGHT = newHeight;
glutPostRedisplay();
}
void checkViewUp()
{
// At v=.25 and v=.75, the x value sign changes
// adjust the view up vector if needed
if(cameraLoc[0] > 0)
{
y_up = -1;
}
else
{
y_up = 1;
}
}
// ascii key and (x,y) of mouse when the key is pressed
void keyboardCb(unsigned char key, int x, int y)
{
switch(key)
{
case 'a':
v += vInc;
printf("\nPressed a, v: %f\n", v);
if(v > 1)
{
v = 0;
}
break;
case 'd':
printf("\nPressed d, v: %f\n", v);
v -= 0.01;
if(v < 0)
{
v = 1;
}
break;
}
// May have changed the camera location, so redraw the scene
glutPostRedisplay();
}
void computeCameraLoc()
{
float pi = 3.14159;
cameraLoc[0] = viewingRadius * cos(2*pi * v);
cameraLoc[1] = viewingRadius * sin(2*pi * v);
cameraLoc[2] = 0;
printf("\ncameraLoc: [%f,%f,%f]\n", cameraLoc[0], cameraLoc[1], cameraLoc[2]);
}
void drawCube()
{
glColor3f(1.0, 0.0, 0.0);
int z = 1;
// front
glBegin(GL_QUADS);
glVertex3i(-1, -1, z);
glVertex3i(1, -1, z);
glVertex3i(1, 1, z);
glVertex3i(-1, 1, z);
glEnd();
// back
glColor3f(0.0, 0.0, 0.0);
z = -1;
glBegin(GL_QUADS);
glVertex3i(-1, -1, z);
glVertex3i(1, -1, z);
glVertex3i(1, 1, z);
glVertex3i(-1, 1, z);
glEnd();
// top
glColor3f(0.0, 1.0, 0.0);
int y = 1;
glBegin(GL_QUADS);
glVertex3i(-1, y, -1);
glVertex3i(1, y, -1);
glVertex3i(1, y, 1);
glVertex3i(-1, y, 1);
glEnd();
// bottom
glColor3f(1.0, 1.0, 0.0);
y = -1;
glBegin(GL_QUADS);
glVertex3i(-1, y, -1);
glVertex3i(1, y, -1);
glVertex3i(1, y, 1);
glVertex3i(-1, y, 1);
glEnd();
// left
glColor3f(0.0, 1.0, 1.0);
int x = 1;
glBegin(GL_QUADS);
glVertex3i(x, 1, 1);
glVertex3i(x, 1, -1);
glVertex3i(x, -1, -1);
glVertex3i(x, -1, 1);
glEnd();
// right
glColor3f(1.0, 0.0, 1.0);
x = -1;
glBegin(GL_QUADS);
glVertex3i(x, 1, 1);
glVertex3i(x, 1, -1);
glVertex3i(x, -1, -1);
glVertex3i(x, -1, 1);
glEnd();
}
void display()
{
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT);
// Compute camera position in world coordinates
computeCameraLoc();
// Check if we need to adjust the direction of view y axis
checkViewUp();
// Set viewing parameters and projection
setupView();
// Draw the cube
drawCube();
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutCreateWindow("Hello world!");
// Setup callbacks
glutReshapeFunc(reshapeCb);
glutKeyboardFunc(keyboardCb);
glutDisplayFunc(display);
init();
glutMainLoop();
return 0;
}
The function checkViewUp will flip the sign of the up vector depending on the sign of the camera’s x position. If I do not do this, then the x and y axes of the view frame get their direction flipped when the camera’s x position sign changes. The effect of their directions changing is that the cube flips upside down when the camera crosses over the
Negating the sign of the y value for the view up vector when the camera’s x sign changes fixes this problem.
Why does this happen and why does flipping the direction of the view up vector fix it? I don’t quite understand the math of this issue. I think it has something to do with the z and y axes being aligned when the camera is directly above the origin, but modelview matrix values make it seem like OpenGL is deciding a new direction for y based on printing out the matrix.
If someone can explain exactly what is happening to cause the flipping and/or why negating the view up y direction fixes it, or point me in the direction of reading about this, then I would appreciate it.