29. OpenGL Bounding Sphere Collision (Version 2.0)

Introduction

Collision detection is a fundamental part of any game or simulation. Without it, you wouldn’t know when two objects interact or collide, such as when a player hits a wall or picks up an item. One simple and efficient way to detect collisions in OpenGL is by using **bounding spheres**.

A bounding sphere is a spherical boundary that surrounds an object. By calculating the distance between the centers of two bounding spheres and comparing it to the sum of their radii, we can determine if a collision has occurred.

In this tutorial, we’ll learn how to implement bounding sphere collision detection, visualize the collision response with color changes, and allow real-time object movement using keyboard controls.

Understanding Bounding Spheres

A bounding sphere is defined by:

  • The center of the sphere (x, y, z coordinates).
  • The radius, which determines the size of the sphere.

The collision detection works as follows:
1. Calculate the distance between the centers of two spheres.
2. Compare this distance to the sum of their radii.
3. If the distance is less than or equal to the sum of the radii, a collision is occurring.

Calculating Distance Between Two Points

The formula to calculate the distance between two points in 3D space is:
d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2}

This formula is the foundation of our collision detection logic.

Collision Detection Logic

We’ll use the above formula to calculate the distance between two spheres. Then, we compare it with the sum of their radii:

void collision(void) {
    d = sqrt(((p1x - p2x) * (p1x - p2x)) + 
             ((p1y - p2y) * (p1y - p2y)) + 
             ((p1z - p2z) * (p1z - p2z)));
}

If a collision occurs, the distance between the two centers will satisfy:
d \leq r_1 + r_2

Visualizing the Collision

To make collisions visible, we’ll change the color of the spheres:

  • Red: Collision detected.
  • Blue: No collision.

Here’s how we define the color logic:

void pointz(void) {
    glPushMatrix();
    if (d <= p2radius + p1radius) {
        glColor3f(1, 0, 0); // Red for collision
    } else {
        glColor3f(0, 0, 1); // Blue for no collision
    }
    glBegin(GL_POINTS);
    glVertex3f(p1x, p1y, p1z); // Draw point 1
    glEnd();
    glPopMatrix();

    glPushMatrix();
    glColor3f(0, 1, 0); // Green for point 2
    glBegin(GL_POINTS);
    glVertex3f(p2x, p2y, p2z); // Draw point 2
    glEnd();
    glPopMatrix();
}

Adding Movement Controls

To test the collision detection, we’ll allow the user to move the spheres using keyboard input:

void keyboard(unsigned char key, int x, int y) {
    // Move sphere 1
    if (key == 'q') { p1z -= 0.1; }
    if (key == 'z') { p1z += 0.1; }
    if (key == 'w') { p1y += 0.1; }
    if (key == 's') { p1y -= 0.1; }
    if (key == 'a') { p1x -= 0.1; }
    if (key == 'd') { p1x += 0.1; }

    // Move sphere 2
    if (key == 'i') { p2y += 0.1; }
    if (key == 'k') { p2y -= 0.1; }
    if (key == 'j') { p2x -= 0.1; }
    if (key == 'l') { p2x += 0.1; }

    // Exit program
    if (key == 27) { exit(0); } // ESC key
}

Tutorial Code

Here’s the complete implementation of the bounding sphere collision detection:

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

// Variables for positions, radius, and distance
GLfloat d;
GLfloat p1x = 0.0, p1y = 0.0, p1z = 0.0; // Point 1 position
GLfloat p2x = 2.0, p2y = 0.0, p2z = 0.0; // Point 2 position
const GLfloat p1radius = 1.0;
const GLfloat p2radius = 0.5;

// Calculate the distance between two points
void collision(void) {
    d = sqrt(((p1x - p2x) * (p1x - p2x)) + 
             ((p1y - p2y) * (p1y - p2y)) + 
             ((p1z - p2z) * (p1z - p2z)));
}

// Render the points and their collision status
void pointz(void) {
    glPushMatrix();
    if (d <= p2radius + p1radius) {
        glColor3f(1, 0, 0); // Collision (red)
    } else {
        glColor3f(0, 0, 1); // No collision (blue)
    }
    glBegin(GL_POINTS);
    glVertex3f(p1x, p1y, p1z); // Point 1
    glEnd();
    glPopMatrix();

    glPushMatrix();
    glColor3f(0, 1, 0); // Point 2 (green)
    glBegin(GL_POINTS);
    glVertex3f(p2x, p2y, p2z);
    glEnd();
    glPopMatrix();
}

// Display callback
void display(void) {
    glClearColor(0.0, 0.0, 0.0, 1.0); // Black background
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

    glPointSize(5); // Set point size
    collision();    // Check for collisions
    pointz();       // Render points

    glutSwapBuffers();
}

// Reshape callback
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);
}

// Main function
int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Bounding Sphere Collision");
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard); // Handle keyboard input
    glutMainLoop();
    return 0;
}

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

  • March 25, 2010
  • 27