24. OpenGL Camera Part 3 (Version 2.0)

Introduction

In this tutorial, we’ll explore how to create a third-person camera system in OpenGL. Unlike the first-person camera, which places the viewer inside the scene, a third-person camera follows a target object from behind. This type of camera is common in adventure and role-playing games, offering a clear view of the character while maintaining player control over movement and rotation.

We’ll build on the concepts introduced in the previous camera tutorials, adding new techniques to handle third-person perspective.

Understanding the Third-Person Camera

A third-person camera system requires two key components:

  • The target object, which the camera follows (e.g., a player or character).
  • The camera offset, which determines the distance and angle of the camera relative to the target.

By combining translations and rotations, we can position the camera behind the target, ensuring the character stays in view.

Defining the Camera Offset

To begin, we define a variable for the camera’s radius, which controls the distance between the camera and the target object:

float cRadius = 10.0f; // Distance from the character to the camera

This value can be adjusted to change the camera’s zoom level or perspective.

Setting Up the Camera Transformations

The camera’s transformations involve three steps:

  1. Translate: Move the camera backward by the radius value to position it behind the character.
  2. Rotate: Apply pitch and yaw rotations to allow the camera to look around the target.
  3. Adjust Scene: Move the entire scene to match the camera’s perspective.

Here’s how we translate the camera:

glTranslatef(0.0f, 0.0f, -cRadius); // Move the camera backward

Next, we rotate the view to simulate camera movement around the target:

glRotatef(xrot, 1.0, 0.0, 0.0); // Rotate up and down
glRotatef(yrot, 0.0, 1.0, 0.0); // Rotate left and right

Drawing the Target Object

The target object (e.g., a character) is drawn at the origin of the camera’s perspective. For this example, we’ll use a red cube to represent the character:

glColor3f(1.0f, 0.0f, 0.0f); // Set color to red
glutSolidCube(2);           // Draw the character

Rendering the Scene

Once the camera transformations are applied, we adjust the scene based on the character’s position:

glTranslated(-xpos, 0.0f, -zpos); // Translate the scene to match the camera's position

Finally, we draw additional objects in the scene, such as cubes, to create a 3D environment.

Maintaining Movement and Controls

The third-person camera retains the movement and strafing controls from the previous tutorials. The character always stays in view, and the camera follows smoothly as the player navigates the environment.

Tutorial Code

Here’s the complete implementation of the third-person camera system:

#include <GL/gl.h>
#include <GL/glut.h>
#include <cstdlib>
#include <cmath>

// Camera and character variables
float xpos = 0, ypos = 0, zpos = 0, xrot = 0, yrot = 0, cRadius = 10.0f;
float lastx, lasty;

// Cube positions
float positionz[10];
float positionx[10];
void cubepositions(void) {
    for (int i = 0; i < 10; i++) {
        positionz[i] = rand() % 5 + 1;
        positionx[i] = rand() % 5 + 1;
    }
}

// Draw cubes
void cube(void) {
    for (int i = 0; i < 10; i++) {
        glPushMatrix();
        glTranslated(-positionx[i + 1] * 10, 0, -positionz[i + 1] * 10);
        glutSolidCube(2);
        glPopMatrix();
    }
}

// Initialize cube positions
void init(void) {
    cubepositions();
}

// Enable OpenGL features
void enable(void) {
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glShadeModel(GL_SMOOTH);
}

// Render scene
void display(void) {
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    enable();

    glLoadIdentity();

    // Camera transformations
    glTranslatef(0.0f, 0.0f, -cRadius);  // Move the camera backward
    glRotatef(xrot, 1.0, 0.0, 0.0);     // Rotate pitch
    glColor3f(1.0f, 0.0f, 0.0f);        // Draw character
    glutSolidCube(2);

    glRotatef(yrot, 0.0, 1.0, 0.0);     // Rotate yaw
    glTranslated(-xpos, 0.0f, -zpos);   // Adjust scene

    glColor3f(1.0f, 1.0f, 1.0f);
    cube(); // Draw cubes

    glutSwapBuffers();
}

// Adjust viewport and perspective
void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
}

// Handle keyboard input
void keyboard(unsigned char key, int x, int y) {
    if (key == 'w') { // Move forward
        float yrotrad = (yrot / 180 * 3.141592654f);
        xpos += float(sin(yrotrad));
        zpos -= float(cos(yrotrad));
    }
    if (key == 's') { // Move backward
        float yrotrad = (yrot / 180 * 3.141592654f);
        xpos -= float(sin(yrotrad));
        zpos += float(cos(yrotrad));
    }
    if (key == 27) { // Exit
        exit(0);
    }
}

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutCreateWindow("Third-Person Camera");
    init();
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutMainLoop();
    return 0;
}

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

  • March 25, 2010
  • 16