Setting up a first person camera in OpenGL to move around in your 3D world
A first person camera is a very useful tool which helps you navigate around in a 3D world. It is mainly used in 3D games ( eg. First person shooters ) and CAD based applications. This post is a walkthrough in which you'll set up a first person camera, and by the end of this post, you'll be able to move and look around in your 3D world. Here is a video clip for demonstration:
If you're working with OpenGL, you are in luck. Here's a list of things that you should already be familiar with:
One obvious thing that you will require is mathematics ( High school level ). The mathematical concepts used in this tutorial are:
Prerequisites
This is a tutorial for the OpenGL API ( I'll use the GLUT library to simplify the coding tasks ) with C++ as the programming language. Although, the mathematical concepts will be (almost) same for any other environment or programming language.If you're working with OpenGL, you are in luck. Here's a list of things that you should already be familiar with:
- Setting up a perspective projection
- Drawing basic geometric shapes
- Translation and Rotation
One obvious thing that you will require is mathematics ( High school level ). The mathematical concepts used in this tutorial are:
- Trigonometry ( if you don't know this, what are you doing here ? )
- Polar coordinates
- Converting polar coordinates to Cartesian coordinates
Initial code
I will not start writing the OpenGL code from scratch, and I'm assuming you already have an OpenGL application which has the following rough set of features:- It should have a 3D projection ( Perspective, although you can also work with orthographic, but I don't think that will look very nice )
- Some 3D objects ( if you haven't drawn anything, how will you know that you're moving ?)
#include <GL/glut.h> #define FPS 60
//width and height of the window ( Aspect ration 16:9 ) const int width = 16*50; const int height = 9*50; void display(); void reshape(int w,int h); void timer(int); void init() { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); } int main(int argc,char**argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(width, height); glutCreateWindow("Projectile Motion - 3D Simulation"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(0,timer,0); //more info about this is given below at definition of timer() glutMainLoop(); return 0; }
/* This function just draws the scene. I used Texture mapping to draw
a chessboard like surface. If this is too complicated for you ,
you can just use a simple quadrilateral */
void draw() { glEnable(GL_TEXTURE_2D); GLuint texture; glGenTextures(1,&texture); unsigned char texture_data[2][2][4] = { 0,0,0,255, 255,255,255,255, 255,255,255,255, 0,0,0,255 }; glBindTexture(GL_TEXTURE_2D,texture); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,2,2,0,GL_RGBA,GL_UNSIGNED_BYTE,texture_data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBegin(GL_QUADS); glTexCoord2f(0.0,0.0); glVertex3f(-50.0,-5.0,-50.0); glTexCoord2f(25.0,0.0); glVertex3f(50.0,-5.0,-50.0); glTexCoord2f(25.0,25.0); glVertex3f(50.0,-5.0,50.0); glTexCoord2f(0.0,25.0); glVertex3f(-50.0,-5.0,50.0); glEnd(); glDisable(GL_TEXTURE_2D); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); draw(); glutSwapBuffers(); } void reshape(int w,int h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60,16.0/9.0,1,75); glMatrixMode(GL_MODELVIEW); }
/*this funtion is used to keep calling the display function periodically
at a rate of FPS times in one second. The constant FPS is defined above and
has the value of 60.
This will keep updating the screen.
*/ void timer(int) { glutPostRedisplay(); glutTimerFunc(1000/FPS,timer,0); }
You can just copy paste this code and start here. I've tried to keep this code as simple as possible and if you do not understand any part of this, please go through a beginners reference for OpenGL first. SO LETS GET STARTED WITH THE REAL STUFF.
Mouse Input
If you have ever played a First Person Shooter game, you'll know how the mouse is used to look around inside the 3D world. Camera turns to the direction you move your mouse. The cursor is usually hidden in this process. But first we need to confine the cursor to the center of the window once the application starts. This is done using glWarpPointer(int x,int y).A call to this function positions the mouse pointer in the position (x,y) relative to the top left corner of the window. So, to place the pointer in the center, the parameters should be width/2 and height/2 respectively. Put this function call inside the init() function. So the whole updated function,
void init() { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glutWarpPointer(width/2,height/2); }
Now when you compile and run your code, you will see that when the window pops open, the pointer is already positioned at the center of the window (although it can be moved ).
Now the question is, how to get input from the motion of the mouse? The simple trick is that you keep calling the glutWarpPointer() function periodically ( perhaps from the timer() function), and also keep noting down the change in position ( if any ) the mouse pointer is going through. If this is going over your head, its not a problem really, you will get all this once you see the code.
We will worry about calculating the position change later, for now lets just put the glutWarpPointer(width/2,height/2) inside the timer() function. Now since the pointer is fixed in the center, you cannot use the mouse to close the window, you will have to use Alt+F4 ( I suppose you already know this ). Or if you want a better interface for your program, you can use keyboard input and assign a key which terminates the program. So, the updated timer function,
void timer(int) { glutPostRedisplay(); glutWarpPointer(width/2,height/2); glutTimerFunc(1000/FPS,timer,0); }
After setting all that up, its time you get introduced to the glutPassiveMotionFunc(void (*callback)(int,int)). According to opengl.org,
glutMotionFunc and glutPassiveMotionFunc set the motion and passive motion callback respectively for the current window. The motion callback for a window is called when the mouse moves within the window while one or more mouse buttons are pressed. The passive motion callback for a window is called when the mouse moves within the window while no mouse buttons are pressed.This description clearly indicates that the passive motion callback is suitable for our situation here. We need to know the mouse position even if no button has been pressed on it. The thing that might be confusing you here is that why use a function to know the mouse position if the pointer is always placed at the center. The fact here is that the pointer is NOT ALWAYS at the center. The position is reset by the timer function every 1/60th of a second. We need to know where the mouse pointer goes between that small interval. Getting back to the code, we need to define a new function to act as the callback function for the passive motion. As you can see from the definition of glutPassiveMotionFunc(void (*callback)(int,int)), the function should take two integer arguments. These are the x and y coordinates of the mouse, which are passed when the callback is called. Now add the following to the main function :
glutPassiveMotionFunc(passive_motion);
Add the following declaration to your code (in the global scope):
void passive_motion(int,int);
Finally, add the function definition. I will now move on to explaining the code that is to go inside its body. Before that, lets go through the math.
To produce the effect of "looking around" in the 3D world, you will of course need to use rotation. You need to store the angles at which the coordinate system is to be rotated, which will be controlled by the mouse input. The effect or looking right or left will be controlled by rotating the coordinate system around the Y axis ( the angle associated with this is called Yaw ), and that of looking up or down will be produced by rotating around X axis (the angle is called called Pitch ) as the center of rotation. Here is an image for demonstration.
It will be convenient to declare the variables for these two angles in the global scope (do it at the top of your program where all other variables are declared, its convenient to keep all the declarations together). Keep the initial values of both these equal to zero.
float pitch = 0.0, yaw= 0.0;
Now inside the passive_motion(int,int), you need to add the code to modify the pitch and yaw according to the mouse pointer displacement within the interval. The displacement of the mouse pointer along Y coordinates ( relative to the screen, not the coordinates of the 3D world ) will control the pitch and displacement along X axis will control the yaw. After adding all the required code inside the function body, it will look something like this,
void passive_motion(int x,int y) { /* two variables to store X and Y coordinates, as observed from the center of the window */ int dev_x,dev_y; dev_x = (width/2)-x; dev_y = (height/2)-y; /* apply the changes to pitch and yaw*/ yaw+=(float)dev_x/10.0; pitch+=(float)dev_y/10.0; }
These deviations are then used to update the angles. Notice that the values have been divided by 10. This is because the deviations are too large and need to be decreased. You can control the sensitivity of the mouse input by changing this factor. Also, the variables have been cast into float before division ( And I don't think there's any need to explain why).
To apply this rotation to the scene, we will use a separate function which will be called camera(). This new function will be called before drawing, and will take care of positioning the camera. Declare this and call it from the display() function. The updated display() function,
void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); camera(); draw(); glutSwapBuffers(); }
Inside the camera function, do the rotation ( You know how to rotate the coordinate system in OpenGL right ? )
void camera() { /*limit the values of pitch between -60 and 70 */ if(pitch>=70) pitch = 70; if(pitch<=-60) pitch=-60; glRotatef(-pitch,1.0,0.0,0.0); // Along X axis glRotatef(-yaw,0.0,1.0,0.0); //Along Y axis }
Remember to do the rotation along X axis before you rotate the world around Y axis. It is really hard to explain why, so you will have to use your own imagination here. Notice the value of angle is taken negative. This is because when you want to look right, the world has to be rotated left ( since you cannot actually rotate the camera ). Similar is the case for pitch ( Up and Down ). The figures below explain this.
Now you should be able to look around in your 3D world. One last thing to do is hiding the cursor. That is done using the following code:
glutSetCursor(GLUT_CURSOR_NONE);Add this in the body of the init() function.
Here's the whole code, in case you missed something.
#include <GL/glut.h> #define FPS 60 //width and height of the window ( Aspect ration 16:9 ) const int width = 16*50; const int height = 9*50; float pitch = 0.0, yaw= 0.0; void display(); void reshape(int w,int h); void timer(int); void passive_motion(int,int); void camera(); void init() { glutSetCursor(GLUT_CURSOR_NONE); //Hide mouse pointer glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glutWarpPointer(width/2,height/2); } int main(int argc,char**argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(width, height); glutCreateWindow("Projectile Motion - 3D Simulation"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutPassiveMotionFunc(passive_motion); glutTimerFunc(0,timer,0); //more info about this is given below at definition of timer() glutMainLoop(); return 0; } /* This function just draws the scene. I used Texture mapping to draw a chessboard like surface. If this is too complicated for you , you can just use a simple quadrilateral */ void draw() { glEnable(GL_TEXTURE_2D); GLuint texture; glGenTextures(1,&texture); unsigned char texture_data[2][2][4] = { 0,0,0,255, 255,255,255,255, 255,255,255,255, 0,0,0,255 }; glBindTexture(GL_TEXTURE_2D,texture); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,2,2,0,GL_RGBA,GL_UNSIGNED_BYTE,texture_data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBegin(GL_QUADS); glTexCoord2f(0.0,0.0); glVertex3f(-50.0,-5.0,-50.0); glTexCoord2f(25.0,0.0); glVertex3f(50.0,-5.0,-50.0); glTexCoord2f(25.0,25.0); glVertex3f(50.0,-5.0,50.0); glTexCoord2f(0.0,25.0); glVertex3f(-50.0,-5.0,50.0); glEnd(); glDisable(GL_TEXTURE_2D); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); camera(); draw(); glutSwapBuffers(); } void reshape(int w,int h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60,16.0/9.0,1,75); glMatrixMode(GL_MODELVIEW); } /*this funtion is used to keep calling the display function periodically at a rate of FPS times in one second. The constant FPS is defined above and has the value of 60 */ void timer(int) { glutPostRedisplay(); glutWarpPointer(width/2,height/2); glutTimerFunc(1000/FPS,timer,0); } void passive_motion(int x,int y) { /* two variables to store X and Y coordinates, as observed from the center of the window */ int dev_x,dev_y; dev_x = (width/2)-x; dev_y = (height/2)-y; /* apply the changes to pitch and yaw*/ yaw+=(float)dev_x/10.0; pitch+=(float)dev_y/10.0; } void camera() { /*limit the values of pitch between -60 and 70 */ if(pitch>=70) pitch = 70; if(pitch<=-60) pitch=-60; glRotatef(-pitch,1.0,0.0,0.0); // Along X axis glRotatef(-yaw,0.0,1.0,0.0); //Along Y axis }
Keyboard Input
Now that you are able to use the mouse to look around, the next thing you'll want is motion. What good is a 3D world if you cannot move around in it? For motion, we are going to use keyboard input. The keys W,A,S and D will be used for forward, leftward,backward and rightward motion respectively. The fundamental concept is that as long as any one of these keys is pressed, the position will keep on changing.
The keyboard input will be handled using the following function:
The keyboard input will be handled using the following function:
void glutKeyboardFunc(void (*func)(unsigned char key,int x, int y));opengl.org describes this function as follows:
glutKeyboardFunc sets the keyboard callback for the current window. When a user types into the window, each key press generating an ASCII character will generate a keyboard callback. The key callback parameter is the generated ASCII character. The state of modifier keys such as Shift cannot be determined directly; their only effect will be on the returned ASCII data. The x and y callback parameters indicate the mouse location in window relative coordinates when the key was pressed. When a new window is created, no keyboard callback is initially registered, and ASCII key strokes in the window are ignored. Passing NULL to glutKeyboardFunc disables the generation of keyboard callbacks.The problem here is that the callback is called only once when the button is pressed down. If we start moving when the button is pressed, we need to let the computer know know when to stop. The motion will be stopped when the button is released. To know when the button is released, we will use another similar callback called glutKeyboardUpFunc(void (*func)(unsigned char key,int x,int y)). This callback is identical to the glutKeyboardFunc(),except it is called when a key is released. The next step is to declare and define the callback functions and register them. What goes inside the body of these callback functions, will be taken care of in a while. The declarations:
void keyboard(unsigned char key,int x,int y); void keyboard_up(unsigned char key,int x,int y);
Register these callbacks from inside the main() function, just like you did for the others:
//inside main() glutKeyboardFunc(keyboard); glutKeyboardUpFunc(keyboard_up);
I will now sketch up the details of how we are going to know which direction to proceed in. You'll need a C style structure which will store 4 Booleans called Forward, Backward, Left and Right. All of these will initially be false. Now, lets suppose the user presses down the W key on his keyboard. As soon as W is pressed down, the Boolean Forward will be set to true and will remain true until the W key is released. The rest of the keys will also work in a similar fashion. The camera() function will keep on checking that which of the Booleans are true and will change the position accordingly. Now that you know the idea behind this, lets get to the code. First things first: define the structure, call it Motion and declare a variable called motion of its type and set the initial values of all the members equal to false.
struct Motion { bool Forward,Backward,Left,Right; }; Motion motion = {false,false,false,false};
Whenever one of the 4 keys is pressed, the direction to which that key corresponds, will be set to true. This will be done inside the keyboard() function as follows
void keyboard(unsigned char key,int x,int y) { switch(key) { case 'W': case 'w': motion.Forward = true; break; case 'A': case 'a': motion.Left = true; break; case 'S': case 's': motion.Backward = true; break; case 'D': case 'd': motion.Right = true; break; } }
The variable key contains the ASCII character value of the key that was pressed, so the switch statement is used to compare its value against the 4 of our keys. Notice I do the comparing for both, uppercase and lowercase. This allows the controls to work regardless of the state of the Caps Lock. Similarly, we will use the keyboard_up() function to set these values to false when the key is released.
void keyboard_up(unsigned char key,int x,int y) { switch(key) { case 'W': case 'w': motion.Forward = false; break; case 'A': case 'a': motion.Left = false; break; case 'S': case 's': motion.Backward = false; break; case 'D': case 'd': motion.Right = false; break; } }
Congratulations ! You are now getting all the input from the keyboard that you require. The final and the most complicated task of this tutorial now, is to use this input to actually "move around".
There are actually two ways to move to a point in space. The first is changing the position of the camera and moving it to that point ( not possible in OpenGL), the other is changing the position of the point and bringing it to the camera. Simply speaking, if you wish to produce the effect of moving forward, you either go forward yourself, or bring the world backwards. So if you want to produce the effect of going to the point (x,y) in space, you can translate the world to the point (-x,-y) instead, and it will be indistinguishable. If you recap, this is the same thing we did for rotation. If you wish to look to the right side, rotate the world to the left. That is why we used the negative values of the angles while rotating.
The following figure will ease up your understanding tasks.
Now you need to combine the 'looking around" and the "moving around", i.e, you need to combine the rotation and the translation.WARNING: Don't mess with the order. Always rotate first and then translate. The diagrams below will explain why.
To store the positions of the camera inside the 3D world, we will declare two variables camX and camZ in the global scope. Set the initial values to 0 as we are going to be at the origin when the application starts.
float camX=0.0,camZ=0.0;
Before we start worrying about the math that will be used to update these two variables, lets add the code for the translation inside the camera() function. The whole camera() function will now look something like this.
void camera() { /*limit the values of pitch between -60 and 70 */ if(pitch>=70) pitch = 70; if(pitch<=-60) pitch=-60; glRotatef(-pitch,1.0,0.0,0.0); // Along X axis glRotatef(-yaw,0.0,1.0,0.0); //Along Y axis glTranslatef(-camX,0.0,-camZ); //new code }
When the user will press the W key, he will expect the camera to move in the direction it is looking. That means while updating the camX and camZ positions, you will need to consider the value of yaw. Since calculations are going to be based on trigonometry, it is obvious that we'll need the sin() and cos() functions which are a part of math.h header. So add the following include statement at the top of your program.
#include <math.h>
The trigonometric functions require the angles in radian measure. So it will be convenient to define a constant that converts degrees to radians on multiplication.
#define TO_RADIANS 3.14/180.0
Now look at the diagrams given below. The base of the red arrow is the current position of the camera and the arrow points in the direction that the camera is supposed to be looking in. The angle that the arrow makes with the vertical ( negative side of Z axis ) is yaw. To calculate the position change, we first calculate the angle that the arrow makes with the positive X axis ( by adding 90 to yaw it ). While moving forward, the component of the arrow along X axis will be cosine of 90 + yaw and similarly the component along Z axis will be sine of 90 + yaw. If you compare the X-Z plane here with the regular X-Y plane, you will notice that the signs of the Z axis are opposite. That's why we will take the negative value of the component along Z axis.
So the overall code is supposed to be:
if(motion.Forward) { camX += cos((yaw+90)*TO_RADIANS)/5.0; camZ -= sin((yaw+90)*TO_RADIANS)/5.0; }
Notice that I divided the whole result by 5. This was to slow down the motion a bit. You can change this value to change the speed of the camera.
When you need to move to the left side, just add 90 more degrees to the angle. Similarly, add 180 for backward motion and subtract 90 for moving right. This entire code will go inside the camera() function. So the function will now look something like this:
void camera() { if(motion.Forward) { camX += cos((yaw+90)*TO_RADIANS)/5.0; camZ -= sin((yaw+90)*TO_RADIANS)/5.0; } if(motion.Backward) { camX += cos((yaw+90+180)*TO_RADIANS)/5.0; camZ -= sin((yaw+90+180)*TO_RADIANS)/5.0; } if(motion.Left) { camX += cos((yaw+90+90)*TO_RADIANS)/5.0; camZ -= sin((yaw+90+90)*TO_RADIANS)/5.0; } if(motion.Right) { camX += cos((yaw+90-90)*TO_RADIANS)/5.0; camZ -= sin((yaw+90-90)*TO_RADIANS)/5.0; } /*limit the values of pitch between -60 and 70 */ if(pitch>=70) pitch = 70; if(pitch<=-60) pitch=-60; glRotatef(-pitch,1.0,0.0,0.0); // Along X axis glRotatef(-yaw,0.0,1.0,0.0); //Along Y axis glTranslatef(-camX,0.0,-camZ); }
So that was all !
You should now have a working First Person Camera. Here is the entire code of the application:
#include <GL/glut.h> #include <math.h> #define FPS 60 #define TO_RADIANS 3.14/180.0 //width and height of the window ( Aspect ratio 16:9 ) const int width = 16*50; const int height = 9*50; float pitch = 0.0, yaw= 0.0; float camX=0.0,camZ=0.0; void display(); void reshape(int w,int h); void timer(int); void passive_motion(int,int); void camera(); void keyboard(unsigned char key,int x,int y); void keyboard_up(unsigned char key,int x,int y); struct Motion { bool Forward,Backward,Left,Right; }; Motion motion = {false,false,false,false}; void init() { glutSetCursor(GLUT_CURSOR_NONE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glutWarpPointer(width/2,height/2); } int main(int argc,char**argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(width, height); glutCreateWindow("Projectile Motion - 3D Simulation"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutPassiveMotionFunc(passive_motion); glutTimerFunc(0,timer,0); //more info about this is given below at definition of timer() glutKeyboardFunc(keyboard); glutKeyboardUpFunc(keyboard_up); glutMainLoop(); return 0; } /* This function just draws the scene. I used Texture mapping to draw a chessboard like surface. If this is too complicated for you , you can just use a simple quadrilateral */ void draw() { glEnable(GL_TEXTURE_2D); GLuint texture; glGenTextures(1,&texture); unsigned char texture_data[2][2][4] = { 0,0,0,255, 255,255,255,255, 255,255,255,255, 0,0,0,255 }; glBindTexture(GL_TEXTURE_2D,texture); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,2,2,0,GL_RGBA,GL_UNSIGNED_BYTE,texture_data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBegin(GL_QUADS); glTexCoord2f(0.0,0.0); glVertex3f(-50.0,-5.0,-50.0); glTexCoord2f(25.0,0.0); glVertex3f(50.0,-5.0,-50.0); glTexCoord2f(25.0,25.0); glVertex3f(50.0,-5.0,50.0); glTexCoord2f(0.0,25.0); glVertex3f(-50.0,-5.0,50.0); glEnd(); glDisable(GL_TEXTURE_2D); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); camera(); draw(); glutSwapBuffers(); } void reshape(int w,int h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60,16.0/9.0,1,75); glMatrixMode(GL_MODELVIEW); } /*this funtion is used to keep calling the display function periodically at a rate of FPS times in one second. The constant FPS is defined above and has the value of 60 */ void timer(int) { glutPostRedisplay(); glutWarpPointer(width/2,height/2); glutTimerFunc(1000/FPS,timer,0); } void passive_motion(int x,int y) { /* two variables to store X and Y coordinates, as observed from the center of the window */ int dev_x,dev_y; dev_x = (width/2)-x; dev_y = (height/2)-y; /* apply the changes to pitch and yaw*/ yaw+=(float)dev_x/10.0; pitch+=(float)dev_y/10.0; } void camera() { if(motion.Forward) { camX += cos((yaw+90)*TO_RADIANS)/5.0; camZ -= sin((yaw+90)*TO_RADIANS)/5.0; } if(motion.Backward) { camX += cos((yaw+90+180)*TO_RADIANS)/5.0; camZ -= sin((yaw+90+180)*TO_RADIANS)/5.0; } if(motion.Left) { camX += cos((yaw+90+90)*TO_RADIANS)/5.0; camZ -= sin((yaw+90+90)*TO_RADIANS)/5.0; } if(motion.Right) { camX += cos((yaw+90-90)*TO_RADIANS)/5.0; camZ -= sin((yaw+90-90)*TO_RADIANS)/5.0; } /*limit the values of pitch between -60 and 70 */ if(pitch>=70) pitch = 70; if(pitch<=-60) pitch=-60; glRotatef(-pitch,1.0,0.0,0.0); // Along X axis glRotatef(-yaw,0.0,1.0,0.0); //Along Y axis glTranslatef(-camX,0.0,-camZ); } void keyboard(unsigned char key,int x,int y) { switch(key) { case 'W': case 'w': motion.Forward = true; break; case 'A': case 'a': motion.Left = true; break; case 'S': case 's': motion.Backward = true; break; case 'D': case 'd': motion.Right = true; break; } } void keyboard_up(unsigned char key,int x,int y) { switch(key) { case 'W': case 'w': motion.Forward = false; break; case 'A': case 'a': motion.Left = false; break; case 'S': case 's': motion.Backward = false; break; case 'D': case 'd': motion.Right = false; break; } }
Thanks for your blog on openGL.Its really amazing and nicely explained.So could you please post more such posts in future.(Post for gaming video that you have created on you tube)
ReplyDeleteI really appreciate your feedback, although i'm not going to do the old stuff here. I'm planning on starting some new series and all those will be availaible here along with the source code.
DeleteHello thanks man this really helped me alot but it would have been cooler if you posted it in YouTube please make more c++ opengl glut videos ;you the only guy in YouTube who posts c++, opengl, glut videos ,,,pls bro post more on this subject...thanks
ReplyDeleteAbsolutely amazing !!!
ReplyDeleteGreat job man, you know how to explain all the stuff and make it easy ;)
I wish my teachers would be like you haha
PD: Happy new year ! :D
I found a way to make the mouse smoother.
ReplyDelete// Top.
int old_x, old_y;
#define mouse_speed 2.0
// Mouse function.
...
if (!old_x || !old_y) {
old_x = dev_x;
old_y = dev_y;
return;
}
x_rate = dev_x - old_x;
y_rate = dev_y - old_y;
/* apply the changes to pitch and yaw*/
yaw += (float)x_rate / mouse_speed;
pitch += (float)y_rate / mouse_speed;
old_x = dev_x;
old_y = dev_y;
hello bro,
Deletei need a help bro
can you do this please help me.
Write a C program to draw a line and show translation, rotation and scaling motion of the line using OpenGL.