Cameras in a Spherical Game World

Started by
-1 comments, last by Eridan 12 years, 2 months ago
Hello everyone!

I've been working on a simple 3D game engine using SDL and OpenGL. There are a lot of things I would like to learn, but right now its the camera that has me stuck. So far I've been using a more-or-less FPS style camera, based on a spherical coordinate system with a fixed up direction.

What I'm trying to do is create a game world centered around a sphere, so that the player can walk on and fly around its surface. To make this work I need a camera that recognizes "up" as meaning "away from the origin" and "down" as meaning "toward the origin". So far my attempts to create such a camera have failed, and I haven't had any luck finding a discussion on how such a system should work.

A few notes before I continue:
- I'm using Z as the vertical axis, so X and Y are horizontal.
- I'm using spherical coordinates (R,E,M) to represent (Radial,Elevation,Azimuth).
- I know radians are more natural, so I will be phasing out the use of degrees in the near future.
- If possible I would like to understand gluLookAt() before trying another method.

Here is what I have so far:

The engine starts by turning the movement of the mouse into a set of spherical coordinates mouseE (elevation) and mouseM (azimuth). In this case radius is irrelevant. This part is working as intended:

void Engine::OnMouseMove(int mX, int mY, double relX, double relY) {
mouseM=mouseM-relX*mouseSpeed;
mouseE=mouseE-relY*mouseSpeed;
if (mouseM<0) {mouseM=mouseM+360;}
if (mouseM>360) {mouseM=mouseM-360;}
if (mouseE<-90) {mouseE=-90;}
if (mouseE>90) {mouseE=90;}
}


Next the engine converts those spherical coordinates into cartesian coordinates, and prepares them for use with gluLookAt(). This part works well for flying around and viewing the game world, but it doesn't orient itself relative to the origin:

//Render each frame using OpenGL
void Engine::Render() {
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

//Set aimREM (relative) and aimXYZ (relative)
aimR=100;
aimE=mouseE*0.0174532925;
aimM=mouseM*0.0174532925;
aimX=aimR*cos(aimM)*cos(aimE);
aimY=aimR*sin(aimM)*cos(aimE);
aimZ=aimR*sin(aimE);

//Offset aimXYZ by eyeXYZ
aimX=aimX+eyeX;
aimY=aimY+eyeY;
aimZ=aimZ+eyeZ;

//Prepare the Camera (eyeX,eyeY,eyeZ,aimX,aimY,aimZ,upX,upY,upZ)
gluLookAt(eyeX,eyeY,eyeZ,aimX,aimY,aimZ,0,0,1);

//Render XYZ Axes using RGB color code
glBegin(GL_LINES);
glColor3f(0.5,0.0,0.0); glVertex3f(0,0,0); glVertex3f(-50000000,0,0);
glColor3f(1.0,0.0,0.0); glVertex3f(0,0,0); glVertex3f(50000000,0,0);
glColor3f(0.0,0.5,0.0); glVertex3f(0,0,0); glVertex3f(0,-50000000,0);
glColor3f(0.0,1.0,0.0); glVertex3f(0,0,0); glVertex3f(0,50000000,0);
glColor3f(0.0,0.0,0.5); glVertex3f(0,0,0); glVertex3f(0,0,-50000000);
glColor3f(0.0,0.0,1.0); glVertex3f(0,0,0); glVertex3f(0,0,50000000);
glEnd(); glColor3f(1,1,1);

//Render game objects here
/*
This is where the game objects go =)
*/
SDL_GL_SwapBuffers();
}


I've been attempting to modify the camera so that up and down are relative to the origin. I think the simplest way to do this is to rotate the aim coordinates around the eye, based on its position relative to the sphere. That way, mouseE and mouseM would use the vector pointing from the origin to the eye in place of the Z axis, and the rest would fall into place. Unfortunately, I've tried multiple algorithms for this, and none of them have actually worked:

//Render each frame using OpenGL
void Engine::Render() {
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

//Convert eyeXYZ to spherical eyeREM
eyeR=sqrt(pow(eyeX,2)+pow(eyeY,2)+pow(eyeZ,2));
eyeE=asin(eyeZ/eyeR);
eyeM=atan2(eyeY,eyeX);

//Calculate upXYZ based on eyeREM
upX=cos(eyeM)*cos(eyeE);
upY=sin(eyeM)*cos(eyeE);
upZ=sin(eyeE);

//Set aimREM (relative) and aimXYZ (relative)
aimR=eyeR;
aimE=mouseE*0.0174532925;
aimM=mouseM*0.0174532925;
aimX=aimR*cos(aimM)*cos(aimE);
aimY=aimR*sin(aimM)*cos(aimE);
aimZ=aimR*sin(aimE);

//Rotate aimXYZ or aimREM to account for global position
/*
Pointing the mouse downward should look directly toward the origin
Pointing the mouse upward should look directly away from the origin
Horizontal motion should rotate the camera around the line from origin to eye
*/

//Offset aimXYZ by eyeXYZ
aimX=aimX+eyeX;
aimY=aimY+eyeY;
aimZ=aimZ+eyeZ;

//Prepare the Camera (eyeX,eyeY,eyeZ,aimX,aimY,aimZ,upX,upY,upZ)
gluLookAt(eyeX,eyeY,eyeZ,aimX,aimY,aimZ,upX,upY,upZ);

//Render XYZ Axes using RGB color code
glBegin(GL_LINES);
glColor3f(0.5,0.0,0.0); glVertex3f(0,0,0); glVertex3f(-50000000,0,0);
glColor3f(1.0,0.0,0.0); glVertex3f(0,0,0); glVertex3f(50000000,0,0);
glColor3f(0.0,0.5,0.0); glVertex3f(0,0,0); glVertex3f(0,-50000000,0);
glColor3f(0.0,1.0,0.0); glVertex3f(0,0,0); glVertex3f(0,50000000,0);
glColor3f(0.0,0.0,0.5); glVertex3f(0,0,0); glVertex3f(0,0,-50000000);
glColor3f(0.0,0.0,1.0); glVertex3f(0,0,0); glVertex3f(0,0,50000000);
glEnd(); glColor3f(1,1,1);

//Render game objects here
/*
This is where the game objects go =)
*/
SDL_GL_SwapBuffers();
}


Three dimensional geometry is relatively new to me, so I'm just trying to make sense of this, and make the camera work in the process. I've posed this as a programming question, but I suppose its really about rotating a spherical coordinate system. Either way, I'm just here to learn.

Any help would be greatly appreciated, especially if it clarifies the geometry of the situation. ~_^

This topic is closed to new replies.

Advertisement