Fly Around an Object

Modified

Overview

How can you view an object from any direction? The most basic approaches are to: turn the object, something you might do to an object in your hand or, move around the object, something appropriate for a large object. Here we will implement the latter, viewing from all directions by flying around an object on the surface of a sphere.

The example essentials are simple. We fly around the object by changing our location on the surface of a sphere that has the object at its center. If we think of the sphere in terms of longitude and latitude, we move left-right by changing longitude and up-down by changing latitude; each can be defined by a position angle.

Sphere

The parametric equation for a sphere centered at (x0,y0,z0) of radius r is:

x = x0 + r * cos(j) * sin(q)

y = y0 + r * sin(j) * sin(q)

z = z0 + r * cos(q)

To fly completely around the object, we'll define: (0q, j 360).

Note that we will switch the usual math orientation of x,y,z axes to correspond with the OpenGL orientation:

x = x0 + r * cos(j) * sin(q)

z = z0 + r * sin(j) * sin(q)

y = y0 + r * cos(q)

Moving on the Sphere

We can move on the sphere surface by changing the position angles. Movement around the sphere can be controlled by moving the mouse left-right to change j and up-down to change q. Translating from mouse (x,y) position in a window to position angles is:

q = 360 / windowHeight * y

j = 360 / windowWidth * x

where  windowWidth and windowHeight are determined by the window dimensions.

For a 360x720 window, each pixel in the x-axis (longitude) corresponds to one degree of rotation. A window x value of 90 results in j = 90 degrees.

Note that (x,y) mouse positions are normally relative to previous positions, unless the mouse moves outside the window and reenters at a different location, so that rotational transitions are relatively smooth.

Viewing from the Sphere

Viewing an object at the center of the sphere requires deciding which way is up. Most of us have the majority of experience where up is the direction your head is pointing. If orbiting the Earth at the equator, up naturally is the positive y-axis if you head is aligned north or the negative y-axis if you head is aligned south. If orbiting the Earth over the poles, up is the positive x-axis if you head is aligned east or the negative x-axis if you head is aligned west.

Up can be defined by a vector between our location on the sphere and a nearby point on the same longitude, always maintaining our body in the same relative position; other definitions are possible.

OpenGL/GLUT C code (by H. Shirokawa)

Combining these ideas into OpenGL is straightforward. The following GLUT mouse handler transforms (x,y) to angle position, computes the eye position on the sphere surface and the up vector.

The display function could use these as parameters to the gluLookAt function:

gluLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, upX, upY, upZ);

void onMouseMove(int x, int y) {

// Mouse point to angle conversion
   theta = (360.0/
winHeight)*y*3.0;    //3.0 rotations possible
   phi = (360.0/winWidth)*x*3.0;

// Restrict the angles within 0~360 deg (optional)
  
if(theta > 360)theta = fmod((double)theta,360.0);
  
if(phi > 360)phi = fmod((double)phi,360.0);

// Spherical to Cartesian conversion.  
// Degrees to radians conversion factor 0.0174532
   eyeX = r * sin(theta*0.0174532) * sin(phi*0.0174532);
   eyeY = r * cos(theta*0.0174532);
   eyeZ = r * sin(theta*0.0174532) * cos(phi*0.0174532);

// Reduce theta slightly to obtain another point on the same longitude line on the sphere.
  
GLfloat dt=1.0;
   GLfloat eyeXtemp = r * sin(theta*0.0174532-dt) * sin(phi*0.0174532);
   GLfloat eyeYtemp = r * cos(theta*0.0174532-dt);
   GLfloat eyeZtemp = r * sin(theta*0.0174532-dt) * cos(phi*0.0174532);

// Connect these two points to obtain the camera's up vector.
  
upX=eyeXtemp-eyeX;
   upY=eyeYtemp-eyeY;
   upZ=eyeZtemp-eyeZ;

   glutPostRedisplay();
}

The display function callback utilizes the gluLookAt() function to produce the modelview transformation.

void display( void ) {
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(eyeX,eyeY,eyeZ,0,0,0,upX, upY, upZ);

   cube();

   glutSwapBuffers();
}

A key callback changes the radius r of the sphere, for example the UP arrow decreases the radius making the object appear smaller. Because perspective projection is used, the square appears larger or smaller as gluLookAt eye position changes.

void onSpecialKey(int key, int x, int y) {
    if(key == GLUT_KEY_UP) r -= 0.1;
    if(key == GLUT_KEY_DOWN) r += 0.1;

    onMouseMove(x, y);
}