Introdução
Neste projeto, criaremos uma animação simples de relógio usando OpenGL e GLUT (Graphics Library Utility Toolkit). Esta animação exibirá um relógio com ponteiros em movimento para representar a hora atual. O relógio será atualizado em tempo real, simulando o movimento dos ponteiros de hora, minuto e segundo. Começaremos configurando os arquivos do projeto e, em seguida, prosseguiremos com o código necessário.
👀 Pré-visualização

🎯 Tarefas
Neste projeto, você aprenderá:
- Como configurar os arquivos e bibliotecas do projeto
- Como criar a janela e inicializar o OpenGL
- Como desenhar o fundo e o contorno do relógio
- Como rotacionar o relógio para que a posição das 12 horas esteja no topo
- Como obter a hora atual e calcular as posições dos ponteiros do relógio
- Como desenhar os ponteiros de hora, minuto e segundo no relógio
- Como redimensionar a janela e exibir o relógio em tempo real
🏆 Conquistas
Após concluir este projeto, você será capaz de:
- Configurar e inicializar OpenGL e GLUT
- Desenhar formas e linhas básicas usando OpenGL
- Rotacionar objetos em OpenGL
- Recuperar a hora atual e usá-la para animar objetos
- Lidar com o redimensionamento da janela e a exibição de gráficos em tempo real
Criar os Arquivos do Projeto
Primeiramente, execute o seguinte comando no terminal para instalar as bibliotecas OpenGL e GLUT:
sudo apt update
sudo apt-get install mesa-utils freeglut3-dev -y
Isso instalará a biblioteca gráfica Mesa 3D, bem como a biblioteca GLUT. Depois disso, você pode compilar programas OpenGL usando gcc.
Em seguida, crie um novo arquivo chamado clock_opengl.c e abra-o no seu editor de código preferido.
cd ~/project
touch clock_opengl.c
Incluir Headers e Definir Variáveis
Em seguida, escreveremos o código passo a passo para implementar este projeto.
#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;
Três constantes inteiras são definidas: WINDOW_WIDTH, WINDOW_HEIGHT e ROTATE_DEGREES. Essas constantes são usadas para definir a largura e a altura da janela e o ângulo de rotação.
Configurar a Janela e Inicializar OpenGL
Nesta etapa, configuraremos o tamanho da janela e inicializaremos OpenGL e 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(): É usado para inicializar a biblioteca GLUT.glutInitDisplayMode(): Define o modo de exibição da janela.GLUT_DOUBLEsignifica usar double buffering, eGLUT_RGBsignifica usar o modo de cor RGB.glutInitWindowSize(): Define o tamanho da janela.glutCreateWindow(): Cria uma janela e define o título da janela paraClock Animation.glutDisplayFunc(): Define a função de desenho para que a funçãodrawClockHandsseja chamada quando um gráfico precisar ser desenhado.glutReshapeFunc(): Define a função de callback para redimensionamento da janela, ou seja, quando o tamanho da janela muda, a funçãoreshapeserá chamada.glutIdleFunc(): Define a função de callback ociosa, ou seja, a funçãoidleserá chamada quando não houver outro processamento de evento.glClearColor(0.0, 0.0, 0.0, 1.0): Define a cor de fundo como preto (valor de cor RGBA).glutMainLoop(): Entra no loop principal do GLUT, que continua em execução, lidando com eventos de janela, desenhando gráficos e entrada do usuário.
Desenhar o Fundo do Relógio
Crie uma função chamada drawClockHands. Adicionaremos código para desenhar o fundo do relógio, incluindo um círculo de cor escura.
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 chamada de função limpa o buffer de cores em preparação para desenhar um novo gráfico.GL_COLOR_BUFFER_BITsignifica limpar o buffer de cores para que o conteúdo do desenho anterior seja limpo antes de desenhar um novo desenho.glBegin(): Esta função indica o início da definição de um elemento (neste caso, um quadrilátero).GL_QUADSsignifica que queremos usar quadrângulos para definir o gráfico.glColor3f(): Define o valor de ponto flutuante da cor de desenho atual para cores RGB (vermelho, verde, azul). Aqui, a cor é definida como cinza escuro, com valores RGB(0.1, 0.1, 0.1)representando cinza escuro.glVertex2f(): Define os cantos de um quadrilátero.
O objetivo aqui é desenhar um retângulo cinza escuro na janela OpenGL como o fundo do relógio. Os quatro vértices definem os limites do retângulo, e o código entre glBegin e glEnd é usado para definir a aparência do gráfico. Isso faz parte do desenho do fundo do relógio, que geralmente é desenhado antes de outros elementos, como o ponteiro das horas, o ponteiro dos minutos, o ponteiro dos segundos, etc.
Rotacionar o Relógio
Adicionaremos código para rotacionar o relógio em 180 graus para que a posição das 12 horas fique no topo.
// Dentro da função drawClockHands()
glPushMatrix();
glRotatef(ROTATE_DEGREES, 0, 0, 1);
glPushMatrix(): Esta é uma função em OpenGL que coloca o estado da matriz atual (geralmente a matriz de visualização do modelo) na pilha para que possa ser restaurado posteriormente. Esta é uma prática comum porque você pode precisar fazer várias transformações de matriz diferentes em um desenho, e você precisa garantir que as transformações subsequentes não afetem os estados anteriores.glRotatef(): Esta é outra função OpenGL que faz a transformação de rotação. Ela rotaciona a matriz de visualização do modelo atual porROTATE_DEGREESem relação ao eixo Z.(0, 0, 1)é a definição do eixo de rotação, que aqui significa rotação em relação ao eixo Z. O primeiro valor é o componente de rotação em relação ao eixo X, o segundo valor é o componente de rotação em relação ao eixo Y, e o terceiro valor é o componente de rotação em relação ao eixo Z.
Desenhar o Contorno do Relógio
Vamos desenhar o contorno do relógio com marcas de escala para cada hora.
// Dentro da função 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();
O objetivo principal aqui é desenhar um contorno circular branco no fundo do relógio, representando a moldura externa do relógio. glLineWidth define a espessura da linha, e o loop entre glBegin e glEnd desenha uma série de pontos em um círculo centrado na origem, que são então conectados para formar um loop fechado, o contorno externo do relógio.
Obter a Hora Atual e Calcular as Posições dos Ponteiros do Relógio
Vamos obter a hora atual e calcular as posições dos ponteiros do relógio: hora, minuto e segundo.
// Dentro da função 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;
Uma variável chamada rawtime é definida para armazenar os dados de tempo bruto (rawtime) obtidos do sistema. time_t é um tipo de dado comumente usado para representar o número de segundos em tempo a partir de um ponto específico no tempo (geralmente 1º de janeiro de 1970) até o presente.
Define um ponteiro para uma estrutura tm chamada timeinfo. A estrutura tm é usada para armazenar os vários componentes das informações de tempo e data, como ano, mês, dia, hora, minuto, segundo, etc.
Use a função time para obter a hora atual do sistema, armazenando os segundos do tempo na variável rawtime. O número de segundos retornado pela função time representa o número de segundos a partir de um ponto específico no tempo (geralmente 1º de janeiro de 1970, também conhecido como um timestamp UNIX) até a hora atual.
Use a função localtime para converter o número de segundos obtidos da função time (armazenado na variável rawtime) em informações concretas de tempo e data, e armazene o resultado em uma estrutura timeinfo. A função localtime converte o número de segundos em informações como ano, mês, dia, hora, minuto, segundo, e assim por diante, e armazena essas informações na estrutura timeinfo.
Obtém o número atual de horas da estrutura timeinfo, em seguida, aplica o módulo 12 para limitar o número de horas a uma escala de 12 horas, tm_hour indica a hora. Obtém o número atual de minutos da estrutura timeinfo, tm_min indica o minuto. Obtém o número atual de segundos da estrutura timeinfo, tm_sec indica o segundo.
Desenhar o Ponteiro das Horas
Vamos desenhar o ponteiro das horas no relógio.
// Dentro da função drawClockHands()
double hourAngle = -(90 + hours * 360 / 12) + (360 / 12) * minutes / 60;
glLineWidth(5.0); // Set line width
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(1.0, 0.0, 0.0); // Hour hand color
glVertex2d(0.5 * cos(hourAngle * M_PI / 180), 0.5 * sin(hourAngle * M_PI / 180));
glEnd();
Primeiramente, os relógios geralmente estão em uma escala de 12 horas, então o número de hours precisa ser convertido para o ângulo no mostrador do relógio. Este cálculo começa mapeando o número de horas de uma escala de 12 horas para uma faixa angular de 360 graus. A contribuição dos minutes (pela posição do ponteiro dos minutos) é então adicionada ao ângulo da hora. O hourAngle resultante representa o ângulo do ponteiro das horas em relação à direção das 12 horas.
Em seguida, define-se um elemento, neste caso um segmento de linha, representando o ponteiro das horas. O ponto inicial do segmento de linha está localizado na coordenada (0, 0), o centro do relógio. O ponto final do segmento de linha, usando as funções trigonométricas cos e sin para calcular a posição final do ponteiro das horas. Os ponteiros de minutos e segundos subsequentes também são desenhados desta forma.
Desenhar o Ponteiro dos Minutos
Vamos adicionar código para desenhar o ponteiro dos minutos no relógio.
// Dentro da função drawClockHands()
double minuteAngle = -(90 + minutes * 360 / 60);
glLineWidth(3.0); // Set line width
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 1.0, 0.0); // Minute hand color
glVertex2d(0.7 * cos(minuteAngle * M_PI / 180), 0.7 * sin(minuteAngle * M_PI / 180));
glEnd();
O mostrador do relógio geralmente tem 60 minutos, então o número de minutes precisa ser convertido para o ângulo no mostrador do relógio. Este cálculo primeiro mapeia o número de minutos para uma faixa angular de 360 graus e subtrai 90 graus para corresponder 0 minutos à direção das 12 horas. O minuteAngle resultante representa o ângulo do ponteiro dos minutos em relação à direção das 12 horas.
Desenhar o Ponteiro dos Segundos
Vamos adicionar código para desenhar o ponteiro dos segundos no relógio.
// Dentro da função drawClockHands()
double secondAngle = -(90 + seconds * 360 / 60);
glLineWidth(1.0); // Set line width
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 0.0, 1.0); // Second hand color
glVertex2d(0.9 * cos(secondAngle * M_PI / 180), 0.9 * sin(secondAngle * M_PI / 180));
glEnd();
Os relógios geralmente têm 60 segundos, então o número de seconds precisa ser convertido para o ângulo no mostrador do relógio. Este cálculo primeiro mapeia o número de segundos para uma faixa angular de 360 graus e subtrai 90 graus para corresponder 0 segundos à direção das 12 horas. O secondAngle resultante representa o ângulo do ponteiro dos segundos em relação à direção das 12 horas.
Finalmente, restauraremos a rotação original e exibiremos a animação do relógio.
// Inside the drawClockHands() function
glPopMatrix();
glutSwapBuffers();
glPopMatrix() é usado para restaurar o estado da matriz previamente salvo, e glutSwapBuffers() é usado para completar o desenho com buffer duplo, renderizando a imagem desenhada na tela para obter efeitos de animação suaves.
Redimensionamento da Janela e Exibição em Tempo Real
Criaremos uma função reshape que é chamada quando o tamanho da janela muda.
void reshape(int w, int h) {
glViewport(0, 0, w, h); // Set the OpenGL viewport for window size adjustments
glMatrixMode(GL_PROJECTION); // Switch to projection matrix mode
glLoadIdentity(); // Reset the current matrix
gluOrtho2D(-1, 1, -1, 1); // Set the orthographic projection matrix
glMatrixMode(GL_MODELVIEW); // Switch back to model-view matrix mode
}
O propósito desta função reshape é ajustar o viewport do OpenGL de acordo com o novo tamanho da janela quando o tamanho da janela muda, e definir a matriz de projeção apropriada para garantir que o gráfico desenhado ainda seja exibido corretamente sob o novo tamanho da janela.
Em seguida, definimos uma função chamada idle para realizar algumas operações quando o programa está ocioso.
void idle() {
glutPostRedisplay(); // Request a redisplay when idle
}
O propósito desta função idle é chamar glutPostRedisplay() quando o programa está ocioso; Para solicitar que o conteúdo da janela seja redesenhado a fim de animar ou atualizar o conteúdo da janela em tempo real.
Compilar e Executar o Projeto
- Abra seu terminal e navegue até o diretório do projeto.
cd ~/project
- Compile o código usando o seguinte comando:
gcc -o clock_opengl clock_opengl.c -lGL -lglut -lGLU -lm
- Execute a aplicação:
./clock_opengl

Resumo
Neste projeto, você aprendeu como criar uma animação de relógio simples usando OpenGL e GLUT. Cobrimos a configuração dos arquivos do projeto, a inicialização da janela e do OpenGL, o desenho do fundo do relógio, a rotação do relógio, o desenho do contorno do relógio, a obtenção da hora atual e o desenho dos ponteiros do relógio. Seguindo estes passos, você pode criar uma animação de relógio básica que exibe a hora atual de maneira visualmente envolvente.



