Creando una animación simple de reloj utilizando OpenGL

CCBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este proyecto, crearemos una animación simple de reloj utilizando OpenGL y GLUT (Graphics Library Utility Toolkit). Esta animación mostrará un reloj con las agujas en movimiento para representar la hora actual. El reloj se actualizará en tiempo real, simulando el movimiento de las agujas de horas, minutos y segundos. Comenzaremos configurando los archivos del proyecto y luego procederemos con el código necesario.

👀 Vista previa

Reloj Opengl

🎯 Tareas

En este proyecto, aprenderás:

  • Cómo configurar los archivos y bibliotecas del proyecto
  • Cómo crear la ventana e inicializar OpenGL
  • Cómo dibujar el fondo y el contorno del reloj
  • Cómo rotar el reloj para que la posición de las 12 en punto quede en la parte superior
  • Cómo obtener la hora actual y calcular las posiciones de las agujas del reloj
  • Cómo dibujar las agujas de horas, minutos y segundos en el reloj
  • Cómo redimensionar la ventana y mostrar el reloj en tiempo real

🏆 Logros

Después de completar este proyecto, podrás:

  • Configurar e inicializar OpenGL y GLUT
  • Dibujar formas y líneas básicas utilizando OpenGL
  • Rotar objetos en OpenGL
  • Recuperar la hora actual y utilizarla para animar objetos
  • Manejar el redimensionamiento de la ventana y la visualización en tiempo real de gráficos

Crear los archivos del proyecto

Primero, ejecuta el siguiente comando en la terminal para instalar las bibliotecas OpenGL y GLUT:

sudo apt update
sudo apt-get install mesa-utils freeglut3-dev -y

Esto instalará la biblioteca de gráficos 3D Mesa así como la biblioteca GLUT. Después de eso, podrás compilar programas OpenGL utilizando gcc.

A continuación, crea un nuevo archivo llamado clock_opengl.c y úbrelo en tu editor de código preferido.

cd ~/project
touch clock_opengl.c
✨ Revisar Solución y Practicar

Incluir encabezados y definir variables

A continuación, escribiremos el código paso a paso para implementar este proyecto.

#include <GL/gl.h>
#include <GL/glut.h>
#include <time.h>
#include <math.h>
#include <stdio.h>

const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
const int ROTATE_DEGREES = 180;

Se definen tres constantes enteras, WINDOW_WIDTH, WINDOW_HEIGHT y ROTATE_DEGREES. Estas constantes se utilizan para definir el ancho y el alto de la ventana y el ángulo de rotación.

✨ Revisar Solución y Practicar

Configurar la ventana e inicializar OpenGL

En este paso, configuraremos el tamaño de la ventana e inicializaremos OpenGL y GLUT.

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutCreateWindow("Clock Animation");

    glutDisplayFunc(drawClockHands);
    glutReshapeFunc(reshape);
    glutIdleFunc(idle);

    glClearColor(0.0, 0.0, 0.0, 1.0);

    glutMainLoop();

    return 0;
}
  • glutInit(): Se utiliza para inicializar la biblioteca GLUT.
  • glutInitDisplayMode(): Establece el modo de visualización de la ventana. GLUT_DOUBLE significa utilizar el doble buffer, y GLUT_RGB significa utilizar el modo de color RGB.
  • glutInitWindowSize(): Establece el tamaño de la ventana.
  • glutCreateWindow(): Crea una ventana y establece el título de la ventana en Clock Animation.
  • glutDisplayFunc(): Establece la función de dibujo para que la función drawClockHands sea llamada cuando se necesita dibujar un gráfico.
  • glutReshapeFunc(): Establece la función de devolución de llamada para el redimensionamiento de la ventana, es decir, cuando el tamaño de la ventana cambia, la función reshape será llamada.
  • glutIdleFunc(): Establece la función de devolución de llamada ociosa, es decir, la función idle será llamada cuando no hay ningún otro procesamiento de eventos.
  • glClearColor(0.0, 0.0, 0.0, 1.0): Establece el color vacío en negro (valor de color RGBA).
  • glutMainLoop(): Ingresa al bucle principal de GLUT, que sigue ejecutándose, manejando eventos de ventanado, dibujando gráficos y la entrada del usuario.
✨ Revisar Solución y Practicar

Dibujar el fondo del reloj

Crea una función llamada drawClockHands, agregaremos código para dibujar el fondo del reloj, incluyendo un círculo de color oscuro.

void drawClockHands() {
    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_QUADS);
    glColor3f(0.1, 0.1, 0.1);
    glVertex2f(-1, -1);
    glVertex2f(1, -1);
    glVertex2f(1, 1);
    glVertex2f(-1, 1);
    glEnd();
}
  • glClear(): Esta llamada a la función limpia el buffer de color en preparación para dibujar un nuevo gráfico. GL_COLOR_BUFFER_BIT significa limpiar el buffer de color para que los contenidos del dibujo anterior se eliminen antes de dibujar un nuevo dibujo.
  • glBegin(): Esta función indica el inicio de la definición de un elemento (en este caso, un cuadrilátero). GL_QUADS significa que queremos utilizar cuadriláteros para definir el gráfico.
  • glColor3f(): Establece el valor de punto flotante del color de dibujo actual a los colores RGB (rojo, verde, azul). Aquí, el color se establece en gris oscuro, con los valores RGB (0.1, 0.1, 0.1) que representan el gris oscuro.
  • glVertex2f(): Define las esquinas de un cuadrilátero.

El propósito aquí es dibujar un rectángulo de color gris oscuro en la ventana de OpenGL como el fondo del reloj. Los cuatro vértices definen los límites del rectángulo, y el código entre glBegin y glEnd se utiliza para definir la apariencia del gráfico. Esto es parte del dibujo del fondo del reloj, que generalmente se dibuja antes de otros elementos como la aguja de horas, la aguja de minutos, la aguja de segundos, etc.

✨ Revisar Solución y Practicar

Girar el reloj

Agregaremos código para girar el reloj 180 grados para que la posición de las 12 en punto quede en la parte superior.

// Dentro de la función drawClockHands()
glPushMatrix();
glRotatef(ROTATE_DEGREES, 0, 0, 1);
  • glPushMatrix(): Esta es una función en OpenGL que coloca el estado actual de la matriz (por lo general, la matriz de vista del modelo) en la pila para que pueda ser restaurado más adelante. Esta es una práctica común porque es posible que se necesiten hacer varias transformaciones de matriz diferentes en un dibujo, y se necesita asegurar de que las transformaciones posteriores no afecten los estados anteriores.

  • glRotatef(): Esta es otra función de OpenGL que realiza la transformación de rotación. Gira la matriz de vista del modelo actual en ROTATE_DEGREES alrededor del eje Z. (0, 0, 1) es la definición del eje de rotación, que aquí significa rotación alrededor del eje Z. El primer valor es el componente de rotación alrededor del eje X, el segundo valor es el componente de rotación alrededor del eje Y, y el tercer valor es el componente de rotación alrededor del eje Z.

✨ Revisar Solución y Practicar

Dibujar el contorno del reloj

Dibujaremos el contorno del reloj con marcas para cada hora.

// Dentro de la función drawClockHands()
glLineWidth(3.0);
glBegin(GL_LINE_LOOP);
for (int i = 0; i < 360; i += 6) {
    double angle = i * M_PI / 180;
    double x = 0.9 * cos(angle);
    double y = 0.9 * sin(angle);
    glColor3f(1.0, 1.0, 1.0);
    glVertex2d(x, y);
}
glEnd();

El principal propósito aquí es dibujar un contorno circular blanco en el fondo del reloj, que representa el marco exterior del reloj. glLineWidth establece el ancho de la línea, y el bucle entre glBegin y glEnd dibuja una serie de puntos en un círculo centrado en el origen, que luego se conectan para formar un bucle cerrado, el contorno exterior del reloj.

✨ Revisar Solución y Practicar

Obtener la hora actual y calcular las posiciones de las agujas del reloj

Obtendremos la hora actual y calcularemos las posiciones de las agujas del reloj: horas, minutos y segundos.

// Dentro de la función drawClockHands()
time_t rawtime;
struct tm* timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);

int hours = timeinfo->tm_hour % 12;
int minutes = timeinfo->tm_min;
int seconds = timeinfo->tm_sec;

Se define una variable llamada rawtime para almacenar los datos de tiempo crudo tomados del sistema. time_t es un tipo de datos comúnmente utilizado para representar el número de segundos en el tiempo desde un punto en el tiempo específico (por lo general, el 1 de enero de 1970) hasta la actualidad.

Se define un puntero a una estructura tm llamada timeinfo. La estructura tm se utiliza para almacenar los diversos componentes de la información de fecha y hora, como año, mes, día, hora, minuto, segundo, etc.

Se utiliza la función time para obtener la hora actual del sistema, almacenando los segundos de la hora en la variable rawtime. El número de segundos devuelto por la función time representa el número de segundos desde un punto en el tiempo específico (por lo general, el 1 de enero de 1970, también conocido como marca de tiempo UNIX) hasta la hora actual.

Se utiliza la función localtime para convertir el número de segundos obtenidos de la función time (almacenados en la variable rawtime) en información concreta de fecha y hora, y almacena el resultado en una estructura timeinfo. La función localtime convierte el número de segundos en información como año, mes, día, hora, minuto, segundo, etc., y almacena esta información en la estructura timeinfo.

Obtiene el número actual de horas de la estructura timeinfo, luego se modula 12 para limitar el número de horas a una escala de 12 horas, tm_hour indica la hora. Obtiene el número actual de minutos de la estructura timeinfo, tm_min indica el minuto. Obtiene el número actual de segundos de la estructura timeinfo, tm_sec indica el segundo.

✨ Revisar Solución y Practicar

Dibujar la aguja de horas

Dibujaremos la aguja de horas en el reloj.

// Dentro de la función drawClockHands()
double hourAngle = -(90 + hours * 360 / 12) + (360 / 12) * minutes / 60;
glLineWidth(5.0); // Establece el ancho de la línea
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(1.0, 0.0, 0.0); // Color de la aguja de horas
glVertex2d(0.5 * cos(hourAngle * M_PI / 180), 0.5 * sin(hourAngle * M_PI / 180));
glEnd();

Primero, los relojes suelen estar en una escala de 12 horas, por lo que el número de horas debe ser convertido al Ángulo en el dial del reloj. Este cálculo comienza mapeando el número de horas de una escala de 12 horas a un rango angular de 360 grados. Luego se agrega la contribución de los minutos (por la posición de la aguja de minutos) al ángulo de la hora. El hourAngle resultante representa el ángulo de la aguja de horas con respecto a la dirección de las 12 en punto.

Luego, se define un elemento, en este caso un segmento de línea, que representa la aguja de horas. El punto de inicio del segmento de línea se encuentra en la coordenada (0, 0), el centro del reloj. El punto final del segmento de línea, utilizando las funciones trigonométricas cos y sin para calcular la posición final de la aguja de horas. Las agujas de minutos y segundos posteriores también se dibujan de esta manera.

✨ Revisar Solución y Practicar

Dibujar la aguja de minutos

Agregaremos código para dibujar la aguja de minutos en el reloj.

// Dentro de la función drawClockHands()
double minuteAngle = -(90 + minutes * 360 / 60);
glLineWidth(3.0); // Establece el ancho de la línea
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 1.0, 0.0); // Color de la aguja de minutos
glVertex2d(0.7 * cos(minuteAngle * M_PI / 180), 0.7 * sin(minuteAngle * M_PI / 180));
glEnd();

El dial del reloj suele ser de 60 minutos, por lo que el número de minutos debe ser convertido al Ángulo en el dial del reloj. Este cálculo primero mapea el número de minutos a un rango angular de 360 grados y lo resta 90 grados para que 0 minutos corresponda a la dirección de las 12 en punto. El minuteAngle resultante representa el Ángulo de la aguja de minutos con respecto a la dirección de las 12 en punto.

✨ Revisar Solución y Practicar

Dibujar la aguja de segundos

Agregaremos código para dibujar la aguja de segundos en el reloj.

// Dentro de la función drawClockHands()
double secondAngle = -(90 + seconds * 360 / 60);
glLineWidth(1.0); // Establece el ancho de la línea
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 0.0, 1.0); // Color de la aguja de segundos
glVertex2d(0.9 * cos(secondAngle * M_PI / 180), 0.9 * sin(secondAngle * M_PI / 180));
glEnd();

Los relojes suelen ser de 60 segundos, por lo que el número de segundos debe ser convertido al Ángulo en la cara del reloj. Este cálculo primero mapea el número de segundos a un rango angular de 360 grados y lo resta 90 grados para que 0 segundos corresponda a la dirección de las 12 en punto. El secondAngle resultante representa el Ángulo de la aguja de segundos con respecto a la dirección de las 12 en punto.

Finalmente, restauraremos la rotación original y mostraremos la animación del reloj.

// Dentro de la función drawClockHands()
glPopMatrix();
glutSwapBuffers();

glPopMatrix() se utiliza para restaurar el estado de la matriz guardado previamente, y glutSwapBuffers() se utiliza para completar el dibujo con doble búfer, renderizando la imagen dibujada en la pantalla para lograr efectos de animación suaves.

✨ Revisar Solución y Practicar

Cambio de tamaño de ventana y visualización en tiempo real

Crearemos una función reshape que se llama cuando el tamaño de la ventana cambia.

void reshape(int w, int h) {
    glViewport(0, 0, w, h); // Establece el viewport de OpenGL para ajustes de tamaño de ventana
    glMatrixMode(GL_PROJECTION); // Cambia al modo de matriz de proyección
    glLoadIdentity(); // Restablece la matriz actual
    gluOrtho2D(-1, 1, -1, 1); // Establece la matriz de proyección ortográfica
    glMatrixMode(GL_MODELVIEW); // Vuelve al modo de matriz de vista de modelo
}

El propósito de esta función reshape es ajustar el viewport de OpenGL de acuerdo con el nuevo tamaño de la ventana cuando el tamaño de la ventana cambia, y establecer la matriz de proyección adecuada para garantizar que el gráfico dibujado todavía se mostrará correctamente bajo el nuevo tamaño de la ventana.

A continuación, definimos una función llamada idle para realizar algunas operaciones cuando el programa está inactivo.

void idle() {
    glutPostRedisplay(); // Solicita una redibujado cuando está inactivo
}

El propósito de esta función idle es llamar a glutPostRedisplay() cuando el programa está inactivo; Para solicitar que se vuelva a dibujar el contenido de la ventana con el fin de animar o actualizar el contenido de la ventana en tiempo real.

✨ Revisar Solución y Practicar

Compilar y ejecutar el proyecto

  1. Abra su terminal y navegue hasta el directorio del proyecto.
cd ~/proyecto
  1. Compile el código usando el siguiente comando:
gcc -o clock_opengl clock_opengl.c -lGL -lglut -lGLU -lm
  1. Ejecute la aplicación:
./clock_opengl
Clock Opengl
✨ Revisar Solución y Practicar

Resumen

En este proyecto, has aprendido cómo crear una animación simple de reloj utilizando OpenGL y GLUT. Cubrimos la configuración de los archivos del proyecto, la inicialización de la ventana y OpenGL, el dibujo del fondo del reloj, la rotación del reloj, el dibujo del contorno del reloj, la obtención de la hora actual y el dibujo de las agujas del reloj. Siguiendo estos pasos, puedes crear una animación básica de reloj que muestre la hora actual de manera visualmente atractiva.