23. OpenGL Camera Part 2 (Version 2.0)

Introduction

Building on the basics of camera movement in OpenGL, this tutorial will demonstrate how to incorporate mouse input and strafe movement for a more immersive and intuitive control system. These features are common in first-person shooter games and can make your OpenGL applications feel more dynamic.

We’ll cover:

  • Implementing strafe movement using keyboard controls.
  • Using the mouse to control the camera’s pitch and yaw.
  • Registering and processing mouse input with GLUT.

By the end of this tutorial, you’ll have a functional first-person camera system.

Adding Strafe Movement

Strafe movement allows the camera to move sideways (left or right) while maintaining its current orientation. This is achieved by using the cosine and sine functions to calculate the direction of movement based on the camera’s yaw angle (yrot).

Here’s the implementation for strafing:

  1. // Move right (D key)  
  2. if (key == 'd') {  
  3.     float yrotrad = (yrot / 180 * 3.141592654f); // Convert yrot to radians  
  4.     xpos += float(cos(yrotrad)) * 0.2;          // Move along X-axis  
  5.     zpos += float(sin(yrotrad)) * 0.2;          // Move along Z-axis  
  6. }  
  7.   
  8. // Move left (A key)  
  9. if (key == 'a') {  
  10.     float yrotrad = (yrot / 180 * 3.141592654f); // Convert yrot to radians  
  11.     xpos -= float(cos(yrotrad)) * 0.2;          // Move along X-axis  
  12.     zpos -= float(sin(yrotrad)) * 0.2;          // Move along Z-axis  
  13. }  

Notice how the same calculations are used, but with opposite signs for xpos and zpos. This ensures the camera moves in the correct direction based on the input.

Mouse Input for Camera Rotation

Mouse input is a natural way to control the camera’s pitch (up/down) and yaw (left/right). In this tutorial, we’ll use GLUT’s glutPassiveMotionFunc to register mouse movements when no buttons are pressed.

Here’s the function for handling mouse movement:

  1. void mouseMovement(int x, int y) {  
  2.     int diffx = x - lastx; // Calculate horizontal movement  
  3.     int diffy = y - lasty; // Calculate vertical movement  
  4.     lastx = x;             // Update the last X position  
  5.     lasty = y;             // Update the last Y position  
  6.   
  7.     xrot += (float)diffy;  // Update pitch  
  8.     yrot += (float)diffx;  // Update yaw  
  9. }  

To enable this functionality, add the following line in your main function:

  1. glutPassiveMotionFunc(mouseMovement); // Register the mouse movement function  

Combining Everything

The following code integrates strafe movement and mouse input into the existing camera system. Use the WASD keys for movement and the mouse for looking around.

Tutorial Code

  1. #include <GL/gl.h>  
  2. #include <GL/glut.h>  
  3. #include <cstdlib>  
  4. #include <cmath>  
  5.   
  6. // Camera variables  
  7. float xpos = 0, ypos = 0, zpos = 0, xrot = 0, yrot = 0;  
  8. float lastx, lasty;  
  9.   
  10. // Cube positions  
  11. float positionz[10];  
  12. float positionx[10];  
  13. void cubepositions(void) {  
  14.     for (int i = 0; i < 10; i++) {  
  15.         positionz[i] = rand() % 5 + 5;  
  16.         positionx[i] = rand() % 5 + 5;  
  17.     }  
  18. }  
  19.   
  20. // Draw cubes  
  21. void cube(void) {  
  22.     for (int i = 0; i < 10; i++) {  
  23.         glPushMatrix();  
  24.         glTranslated(-positionx[i + 1] * 10, 0, -positionz[i + 1] * 10);  
  25.         glutSolidCube(2);  
  26.         glPopMatrix();  
  27.     }  
  28. }  
  29.   
  30. // Initialize settings  
  31. void init(void) {  
  32.     cubepositions();  
  33. }  
  34.   
  35. // Enable lighting and depth testing  
  36. void enable(void) {  
  37.     glEnable(GL_DEPTH_TEST);  
  38.     glEnable(GL_LIGHTING);  
  39.     glEnable(GL_LIGHT0);  
  40.     glShadeModel(GL_SMOOTH);  
  41. }  
  42.   
  43. // Camera setup  
  44. void camera(void) {  
  45.     glRotatef(xrot, 1.0, 0.0, 0.0);  
  46.     glRotatef(yrot, 0.0, 1.0, 0.0);  
  47.     glTranslated(-xpos, -ypos, -zpos);  
  48. }  
  49.   
  50. // Display function  
  51. void display(void) {  
  52.     glClearColor(0.0, 0.0, 0.0, 1.0);  
  53.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  54.     glLoadIdentity();  
  55.     camera();  
  56.     enable();  
  57.     cube();  
  58.     glutSwapBuffers();  
  59. }  
  60.   
  61. // Reshape function  
  62. void reshape(int w, int h) {  
  63.     glViewport(0, 0, (GLsizei)w, (GLsizei)h);  
  64.     glMatrixMode(GL_PROJECTION);  
  65.     glLoadIdentity();  
  66.     gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 1000.0);  
  67.     glMatrixMode(GL_MODELVIEW);  
  68. }  
  69.   
  70. // Keyboard controls  
  71. void keyboard(unsigned char key, int x, int y) {  
  72.     float yrotrad;  
  73.     if (key == 'w') { // Move forward  
  74.         yrotrad = (yrot / 180 * 3.141592654f);  
  75.         xpos += float(sin(yrotrad));  
  76.         zpos -= float(cos(yrotrad));  
  77.     }  
  78.     if (key == 's') { // Move backward  
  79.         yrotrad = (yrot / 180 * 3.141592654f);  
  80.         xpos -= float(sin(yrotrad));  
  81.         zpos += float(cos(yrotrad));  
  82.     }  
  83.     if (key == 'd') { // Strafe right  
  84.         yrotrad = (yrot / 180 * 3.141592654f);  
  85.         xpos += float(cos(yrotrad)) * 0.2;  
  86.         zpos += float(sin(yrotrad)) * 0.2;  
  87.     }  
  88.     if (key == 'a') { // Strafe left  
  89.         yrotrad = (yrot / 180 * 3.141592654f);  
  90.         xpos -= float(cos(yrotrad)) * 0.2;  
  91.         zpos -= float(sin(yrotrad)) * 0.2;  
  92.     }  
  93.     if (key == 27) { // Exit program  
  94.         exit(0);  
  95.     }  
  96. }  
  97.   
  98. // Mouse movement  
  99. void mouseMovement(int x, int y) {  
  100.     int diffx = x - lastx;  
  101.     int diffy = y - lasty;  
  102.     lastx = x;  
  103.     lasty = y;  
  104.     xrot += (float)diffy;  
  105.     yrot += (float)diffx;  
  106. }  
  107.   
  108. int main(int argc, char **argv) {  
  109.     glutInit(&argc, argv);  
  110.     glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);  
  111.     glutInitWindowSize(500, 500);  
  112.     glutCreateWindow("Advanced OpenGL Camera");  
  113.     init();  
  114.     glutDisplayFunc(display);  
  115.     glutIdleFunc(display);  
  116.     glutReshapeFunc(reshape);  
  117.     glutPassiveMotionFunc(mouseMovement);  
  118.     glutKeyboardFunc(keyboard);  
  119.     glutMainLoop();  
  120.     return 0;  
  121. }  

If you have any questions or run into issues, feel free to email me at swiftless@gmail.com. Happy coding!

  • March 25, 2010
  • 14